نوع داده None در پایتون

امیرحسین بیگدلو 4 ماه قبل

 

اگر با زبان های برنامه نویسی دیگر مانند C یا جاوا تجربه دارید، احتمالاً مفهوم null را شنیده اید. بسیاری از زبان‌ها از این برای نشان دادن اشاره‌گری استفاده می‌کنند که به چیزی اشاره نمی‌کند، برای مشخص کردن زمانی که متغیری خالی است، یا برای علامت‌گذاری پارامترهای پیش‌فرضی که هنوز ارائه نکرده‌اید. null اغلب در آن زبان ها 0 تعریف می شود، اما null در پایتون متفاوت است.

 

پایتون از کلمه کلیدی None برای تعریف اشیا و متغیرهای null استفاده می کند. در حالی که None برخی از اهداف مشابه را در زبان های دیگر انجام می دهد، اما کاملاً موجود دیگری است. در پایتون، None یک شی و یک شهروند درجه یک است!

 

دوره پیشنهادی: دوره آموزش پایتون (python)

 

 #  درک None در پایتون

None مقداری است که فانکشن‌ها در زمانی که return وجود ندارد، ارائه میدهند:

>>> def has_no_return():
...     pass
>>> has_no_return()
>>> print(has_no_return())
None

 

وقتی has_no_return() را فراخوانی میکنید، هیچ خروجی برای دیدن شما وجود ندارد. با این حال، وقتی آنرا چاپ می‌کنید، None پنهانی را که برمی‌گرداند می‌بینید.

 

در واقع، None آنقدر به‌عنوان یک مقدار بازگشتی ظاهر می‌شود که REPL پایتون None را چاپ نمی‌کند مگر اینکه صریحاً به آن بگویید:

>>> None
>>> print(None)
None

 

None به خودی خود خروجی ندارد، اما چاپ آن None را در کنسول نمایش می دهد.

 

جالب اینجاست که خود print() هیچ مقدار بازگشتی ندارد. اگر سعی کنید یک فراخوان برای print() چاپ کنید، None را دریافت خواهید کرد:

>>> print(print("Hello, World!"))
Hello, World!
None

 

ممکن است عجیب به نظر برسد، اما print(print("...")) مقدار None را به شما نشان می دهد که print() داخلی برمی گرداند.

 

None نیز اغلب به عنوان سیگنالی برای پارامترهای گم شده یا پیش فرض استفاده می شود. به عنوان مثال، None دو بار در اسناد برای list.sort ظاهر می شود:

>>> help(list.sort)
Help on method_descriptor:

sort(...)
    L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*

 

در اینجا، None مقدار پیش‌فرض برای پارامتر key و همچنین type hint برای مقدار بازگشتی است. خروجی دقیق help می تواند از پلت فرمی به پلتفرم دیگر متفاوت باشد. زمانی که این دستور را در مفسر خود اجرا می کنید، ممکن است خروجی متفاوتی دریافت کنید، اما مشابه خواهد بود.

 

ویدیو مرتبط: ویدیو انواع None در پایتون

 

 #  استفاده از None در پایتون

اغلب، از None به عنوان بخشی از مقایسه استفاده می کنید. یک مثال زمانی است که باید بررسی کنید و ببینید آیا نتیجه یا پارامتری None است یا خیر. نتیجه ای را که از re.match می گیرید بگیرید. آیا عبارت منظم شما با رشته داده شده مطابقت داشت؟ یکی از این دو نتیجه را خواهید دید:

  1. بازگشت آبجکت match: عبارت منظم شما پیدا شد.
  2. بازگشت None: عبارت منظم شما پیدا نشد.

 

در بلوک کد زیر، در حال آزمایش هستید که آیا الگوی "Goodbye" با یک رشته مطابقت دارد یا خیر:

>>> import re
>>> match = re.match(r"Goodbye", "Hello, World!")
>>> if match is None:
...     print("It doesn't match.")
It doesn't match.

 

در اینجا، از None استفاده می کنید تا آزمایش کنید که آیا الگو با رشته "Hello, World!" مطابقت دارد یا خیر. این بلوک کد یک قانون مهم را نشان می دهد که باید هنگام بررسی None به خاطر داشته باشید:

  1. از عبارات شناسایی is و is not استفاده کنید.
  2. از عبارات مقایسه‌ای == و =! استفاده نکنید.

 

هنگام مقایسه اشیاء تعریف شده توسط کاربر که آنها را میتوان بازنویسی کرد، عملگرهای برابری را می توان فریب داد:

>>> class BrokenComparison:
...     def __eq__(self, other):
...         return True
>>> b = BrokenComparison()
>>> b == None  # Equality operator
True
>>> b is None  # Identity operator
False

 

در اینجا عملگر برابری == پاسخ اشتباه را برمی‌گرداند. از طرف دیگر، اپراتور هویت را نمی توان فریب داد زیرا نمی توانید آن را بازنویسی کنید.

 

None برابر false است، به این معنی که None مقدار True نیست. اگر تنها چیزی که می خواهید بدانید این است که آیا نتیجه نادرست است، آزمایشی مانند زیر کافی است:

>>> some_result = None
>>> if some_result:
...     print("Got a result!")
... else:
...     print("No result.")
...
No result.

 

خروجی به شما نشان نمی دهد که some_result دقیقاً None است، فقط نشان میدهد که نادرست است. اگر باید بدانید که آیا شیء None دارید یا نه، پس از is و is not  استفاده کنید.

 

آبجکت‌های زیر به معنی نادرست هستند:

  • لیست خالی
  • دیکشری خالی
  • set خالی
  • رشته خالی
  • 0
  • False

 

ویدیو مرتبط: ویدیو تفاوت بین is و == در پایتون

 

 #  تعریف None در پایتون

در برخی از زبان ها، متغیرها با یک اعلان زنده می شوند. متغیرها مجبور نیستند یک مقدار اولیه به آنها اختصاص داده شود. در آن زبان‌ها، مقدار پیش‌فرض اولیه برای برخی از انواع متغیرها ممکن است تهی باشد. اما در پایتون، متغیرها با دستورهای انتساب زنده می شوند. به بلوک کد زیر نگاهی بیندازید:

>>> print(bar)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined
>>> bar = None
>>> print(bar)
None

 

در اینجا می بینید که متغیری با مقدار None با متغیر تعریف نشده متفاوت است. همه متغیرها در پایتون با انتساب به وجود می آیند. یک متغیر فقط در صورتی زندگی را به صورت null در پایتون شروع می کند که None را به آن اختصاص دهید.

 

مقاله پیشنهادی: معرفی کلمات کلیدی در پایتون

 

 #  استفاده از None به عنوان مقدار پیشفرض پارامترها در پایتون

اغلب، شما از None به عنوان مقدار پیش فرض برای یک پارامتر اختیاری استفاده می کنید. دلیل بسیار خوبی برای استفاده از None در اینجا به جای یک نوع قابل تغییر مانند لیست وجود دارد. تابعی مثل این را تصور کنید:

def bad_function(new_elem, starter_list=[]):
    starter_list.append(new_elem)
    return starter_list

 

bad_function() حاوی یک شگفتی بد است. وقتی آن را با یک لیست موجود فراخوانی می کنید خوب کار می کند:

>>> my_list = ['a', 'b', 'c']
>>> bad_function('d', my_list)
['a', 'b', 'c', 'd']

 

در اینجا، بدون هیچ مشکلی "d" را به انتهای لیست اضافه می کنید.

 

اما اگر این تابع را چند بار بدون پارامتر starter_list فراخوانی کنید، رفتار نادرستی را مشاهده می کنید:

>>> bad_function('a')
['a']
>>> bad_function('b')
['a', 'b']
>>> bad_function('c')
['a', 'b', 'c']

 

مقدار پیش‌فرض starter_list تنها یک بار در زمان تعریف تابع ارزیابی می‌شود، بنابراین هر بار که لیست موجود را ارسال نکنید، کد از آن دوباره استفاده می‌کند.

 

راه درست برای ساخت این تابع این است که از None به عنوان مقدار پیش فرض استفاده کنید، سپس آن را آزمایش کنید و در صورت نیاز یک لیست جدید را نمونه برداری کنید:

>>> def good_function(new_elem, starter_list=None):
...     if starter_list is None:
...         starter_list = []
...     starter_list.append(new_elem)
...     return starter_list
...

>>> good_function('e', my_list)
['a', 'b', 'c', 'd', 'e']

>>> good_function('a')
['a']

>>> good_function('b')
['b']

>>> good_function('c')
['c']

 

()good_function با ایجاد یک لیست جدید با هر فراخوانی که در آن لیست موجود را ارسال نمی کنید، همانطور که می خواهید رفتار می کند. این کار می کند زیرا کد شما هر بار که تابع را با پارامتر پیش فرض فراخوانی می کند، خطوط 2 و 3 را اجرا می کند.

 

مقاله پیشنهادی: معرفی کتابخانه‌های sql پایتون

 

 #  استفاده از None به عنوان مقدار خالی در پایتون

وقتی None یک شی ورودی معتبر باشد، چه می‌کنید؟ به عنوان مثال، اگر ()good_function بتواند یک عنصر را به لیست اضافه کند یا نه، و None یک عنصر معتبر برای افزودن باشد، چه؟ در این مورد، می توانید یک کلاس را به طور خاص برای استفاده به عنوان پیش فرض تعریف کنید، در حالی که از None متمایز هستید:

>>> class DontAppend: pass
...
>>> def good_function(new_elem=DontAppend, starter_list=None):
...     if starter_list is None:
...         starter_list = []
...     if new_elem is not DontAppend:
...         starter_list.append(new_elem)
...     return starter_list
...
>>> good_function(starter_list=my_list)
['a', 'b', 'c', 'd', 'e']
>>> good_function(None, my_list)
['a', 'b', 'c', 'd', 'e', None]

 

در اینجا، کلاس DontAppend به عنوان سیگنالی برای اضافه نشدن عمل می کند، بنابراین برای آن به None نیاز ندارید. این شما را آزاد می کند تا در صورت تمایل None را اضافه کنید.

 

شما می توانید از این تکنیک زمانی استفاده کنید که None برای مقادیر برگشتی نیز امکان پذیر باشد. به عنوان مثال، اگر کلیدی در دیکشنری یافت نشد، dict.get به طور پیش‌فرض، None را برمی‌گرداند. اگر None یک مقدار معتبر در دیکشنری شما بود، می توانید dict.get را به این صورت فراخوانی کنید:

>>> class KeyNotFound: pass
...
>>> my_dict = {'a':3, 'b':None}
>>> for key in ['a', 'b', 'c']:
...     value = my_dict.get(key, KeyNotFound)
...     if value is not KeyNotFound:
...         print(f"{key}->{value}")
...
a->3
b->None

 

در اینجا شما یک کلاس سفارشی KeyNotFound را تعریف کرده اید. اکنون، به جای برگرداندن None وقتی کلیدی در دیکشنری نیست، می‌توانید KeyNotFound را برگردانید. وقتی که این مقدار واقعی در دیکشنری است، می توانید None را برگردانید.

 

مقاله مرتبط: درک traceback پایتون

 

 #  رمزگشایی None در traceback پایتون

وقتی NoneType در traceback شما ظاهر می شود، به این معنی است که چیزی که انتظار نداشتید None باشد، در واقع None بوده است و سعی کرده اید به گونه ای از آن استفاده کنید که امکان پذیر نبوده. تقریباً همیشه، به این دلیل است که سعی می‌کنید متدی را روی آن فراخوانی کنید.

 

به عنوان مثال، شما در بالا بارها append() را در my_list فراخوانی کردید، اما اگر my_list به نحوی به چیزی غیر از یک لیست تبدیل شود، append() ناموفق خواهد بود:

>>> my_list.append('f')
>>> my_list
['a', 'b', 'c', 'd', 'e', None, 'f']
>>> my_list = None
>>> my_list.append('g')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'append'

 

در اینجا، کد شما AttributeError بسیار رایج را ایجاد میکند زیرا شی my_list، دیگر یک لیست نیست. شما آن را روی None تنظیم کرده اید، که نمی داند چگونه append() را اضافه کند، بنابراین کد یک استثنا ایجاد می کند.

 

هنگامی که یک traceback مانند این را در کد خود مشاهده کردید، ابتدا به دنبال مشخصه ای باشید که خطا را ایجاد کرده است. در اینجا، append(). از آنجا، شیئی را که سعی کردید آن را فراخوانی کنید، خواهید دید. در این مورد، my_list است، همانطور که می توانید از کد درست بالای traceback متوجه شوید. در نهایت، بفهمید که چگونه آن شی به None تبدیل شده و اقدامات لازم را برای اصلاح کد خود انجام دهید.

 

ویدوی پیشنهادی: ویدیو استفاده از آبجکت های mutable به عنوان مقدار پیشفرض آرگومان ها در پایتون

 

 #  بررسی برای None در پایتون

دو مورد بررسی نوع داده وجود دارد که در پایتون به null مرتبط است. اولین مورد زمانی است که None را برمی‌گردانید:

>>> def returns_None() -> None:
...     pass

 

این حالت شبیه زمانی است که شما اصلاً دستور بازگشتی ندارید که به طور پیش فرض None را برمی گرداند.

 

مورد دوم کمی چالش برانگیزتر است. جایی است که مقداری را می گیرید یا برمی گردانید که ممکن است None باشد، اما ممکن است نوع دیگری باشد. این مورد مانند کاری است که با re.match در بالا انجام دادید، که یا یک شی Match یا None را برگرداند.

 

این فرآیند برای پارامترها مشابه است:

from typing import Any, List, Optional
def good_function(new_elem:Any, starter_list:Optional[List]=None) -> List:
    pass

 

شما ()good_function را از بالا تغییر می دهید و گزینه Optional را از typing وارد می کنید تا یک [Match] اختیاری را برگردانید.

 

 

 #  نتیجه گیری

None یک ابزار قدرتمند در جعبه ابزار پایتون است. مانند True و False، مقدار None یک کلمه کلیدی تغییرناپذیر است. به عنوان null در پایتون، شما از آن برای علامت گذاری مقادیر و نتایج از دست رفته و حتی پارامترهای پیش فرض استفاده می کنید که در آن گزینه بسیار بهتری نسبت به انواع قابل تغییر است.

مطالب مشابه



مونگارد