[Python, Программирование] Разбираемся с not в Python (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет, Хабр. В преддверии старта курса «Python Developer. Professional» подготовили традиционный перевод полезного материала.
Также приглашаем всех желающих посетить открытый вебинар на тему «Визуализация данных с помощью matplotlib».
В этой статье из серии про синтаксический сахар в Python я займусь на первый взгляд очень простым синтаксисом, но на самом деле, чтобы разобраться в механике его работы, нужно погрузиться вглубь на несколько слоев. Мы будем говорить о not.Определение звучит на первый взгляд очень просто:Оператор not выдает True, если его аргумент False, и False в противоположном случае.Достаточно просто, не так ли? Но когда вы начинаете разбираться в том, что считать «истинным» или «ложным», вы быстро понимаете, что есть приличное количество вещей, подходящих под эти определения.(Как и в других статьях этой серии, код на С предназначен для тех, кто хочет пройти путь «по хлебным крошкам», но вы можете пропустить его, если хотите)Реализация notЕсли вы посмотрите на байткод, то заметите единственный опкод, относящийся к not – это UNARY_NOT. Байткод для not a:
>>> import dis
>>> def spam(): not a
...
>>> dis.dis(spam)
1 0 LOAD_GLOBAL 0 (a)
2 UNARY_NOT
4 POP_TOP
6 LOAD_CONST 0 (None)
8 RETURN_VALUE
Реализация UNARY_NOT по сути вызывает функцию С, которая называется PyObject_IsTrue() и возвращает обратное переданному значение: True для False, False для True.Реализация опкода UNARY_NOT из Python/ceval.c:
case TARGET(UNARY_NOT): {
PyObject *value = TOP();
int err = PyObject_IsTrue(value);
Py_DECREF(value);
if (err == 0) {
Py_INCREF(Py_True);
SET_TOP(Py_True);
DISPATCH();
}
else if (err > 0) {
Py_INCREF(Py_False);
SET_TOP(Py_False);
DISPATCH();
}
STACK_SHRINK(1);
goto error;
}
Определение того, что такое TrueВся хитрость нашего разбора not начинается с определения того, что такое True. Если посмотреть на реализацию PyObject_IsTrue() на языке Си, станет видно, что существует несколько возможных способов выяснить истинность объекта.Реализация PyObject_IsTrue():
/* Test a value used as condition, e.g., in a for or if statement.
Return -1 if an error occurred */
int
PyObject_IsTrue(PyObject *v)
{
Py_ssize_t res;
if (v == Py_True)
return 1;
if (v == Py_False)
return 0;
if (v == Py_None)
return 0;
else if (v->ob_type->tp_as_number != NULL &&
v->ob_type->tp_as_number->nb_bool != NULL)
res = (*v->ob_type->tp_as_number->nb_bool)(v);
else if (v->ob_type->tp_as_mapping != NULL &&
v->ob_type->tp_as_mapping->mp_length != NULL)
res = (*v->ob_type->tp_as_mapping->mp_length)(v);
else if (v->ob_type->tp_as_sequence != NULL &&
v->ob_type->tp_as_sequence->sq_length != NULL)
res = (*v->ob_type->tp_as_sequence->sq_length)(v);
else
return 1;
/* if it is negative, it should be either -1 or -2 */
return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}
Если разбираться в реализации на С, то правило выглядит так:
- Если True, то True
- Если False, то False
- Если None, то False
- То, что возвращает bool, до тех пор, пока возвращаемый объект является подклассом bool (то, что показывает вызов nb_bool)
- Вызов len() на объекте (то, за что отвечают вызовы mp_length и sq_length):
- Если больше 0, то True
- В противном случае False
- Если ничего из вышеперечисленного не подходит, то True
Все правила 1-3 и 6 достаточно понятны, а вот правила 4 и 5 требуют углубления в детали.boolОпределение специального/волшебного метода bool говорит нам, что метод используется для «реализации проверки истинности значений» и должен возвращать либо True, либо False.len()Встроенная функция len() возвращает целое число, представляющее количество элементов в контейнере. Реализация вычисления длины объекта представлена слотами sq_length (длина последовательностей) и mp_length (длина словарей/мэпов).Легко подумать, что к объекту можно просто обратиться и запросить его длину, но тут есть два слоя.len Первый слой – это специальный/волшебный метод len. Как и следовало ожидать, он «должен возвращать длину объекта, целое число >= 0». Но дело в том, что «целочисленный» не означает int, а означает объект, который вы можете «преобразовать без потерь»… к «целочисленному объекту». И как же выполнить это преобразование?index«Чтобы без потерь преобразовать численный объект в целочисленный», используется специальный/волшебный метод index. В частности, для обработки преобразования используется функция PyNumber_Index(). Эта функция слишком длинная, чтобы ее сюда вставлять, но делает она следующее:
- Если аргумент является экземпляром класса int, вернуть его
- В противном случае вызвать index на объекте
- Если index возвращает конкретный экземпляр класса int, вернуть его (технически возвращать подкласс не рекомендуется, но давайте оставим это в прошлом).
- В противном случае вернуть TypeError.
На уровне Python это делается с помощью operator.index(). К сожалению, здесь не реализуется семантика PyNumber_Index(), поэтому на самом деле с точки зрения not и len, функция работает немного неточно. Если бы она все же была реализована, то выглядела бы так:Реализация PyNumber_Index() на Python:
def index(obj: Object, /) -> int:
"""Losslessly convert an object to an integer object.
If obj is an instance of int, return it directly. Otherwise call __index__()
and require it be a direct instance of int (raising TypeError if it isn't).
"""
# https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L1260-L1302
if isinstance(obj, int):
return obj
length_type = builtins.type(obj)
try:
__index__ = _mro_getattr(length_type, "__index__")
except AttributeError:
msg = (
f"{length_type!r} cannot be interpreted as an integer "
"(must be either a subclass of 'int' or have an __index__() method)"
)
raise TypeError(msg)
index = __index__(obj)
# Returning a subclass of int is deprecated in CPython.
if index.__class__ is int:
return index
else:
raise TypeError(
f"the __index__() method of {length_type!r} returned an object of "
f"type {builtins.type(index).__name__!r}, not 'int'"
)
Реализация len()Еще один интересный факт о реализации len(): она всегда возвращает конкретный int. Так, несмотря на то, что index() или len() могут возвращать подкласс, ее реализация на уровне С через PyLong_FromSsize_t() гарантирует, что всегда будет возвращаться конкретный экземпляр int.В противном случае len() будет проверять, что возвращают len() и index (), например, является ли возвращаемый объект подклассом int, больше ли значение или равно 0 и т.д. Таким образом, вы можете реализовать len() так:
def len(obj: Object, /) -> int:
"""Return the number of items in a container."""
# https://github.com/python/cpython/blob/v3.8.3/Python/bltinmodule.c#L1536-L1557
# https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L45-L63
# https://github.com/python/cpython/blob/v3.8.3/Objects/typeobject.c#L6184-L6209
type_ = builtins.type(obj)
try:
__len__ = _mro_getattr(type_, "__len__")
except AttributeError:
raise TypeError(f"type {type!r} does not have a __len__() method")
length = __len__(obj)
# Due to len() using PyObject_Size() (which returns Py_ssize_t),
# the returned value is always a direct instance of int via
# PyLong_FromSsize_t().
index = int(_index(length))
if index < 0:
raise ValueError("__len__() should return >= 0")
else:
return index
Реализация operator.truth()Во многих языках программирования при определении операции not, распространенной идиомой является превращение объекта в его сравнительное логическое значение с помощью передачи его в not дважды – через not not. Первый раз, чтобы получить инвертированное логическое значение, и второй раз, чтобы инвертировать инверсию, и получить логическое значение, которое вы хотели изначально.В Python нам не нужна эта идиома. Спасибо bool() (а конкретно bool.new()), за то, что у нас есть вызов функции, который мы можем использовать для получения конкретного логического значения, а именно operator.truth(). Если вы посмотрите на этот метод, то увидите, что он использует PyObject_IsTrue() для определения логического значения объекта. Посмотрев slot_nb_bool (), вы увидите, что в конечном итоге он делает то же, что и PyObject_IsTrue(). То есть, если мы можем реализовать аналог PyObject_IsTrue(), то можем определить, какое логическое значение имеет объект. По старой схеме и с тем, что мы узнали только что, мы можем реализовать operator.truth() для этой логики (я предпочитаю не реализовывать bool, поскольку не хочу реализовывать все его численные функции, и не придумал хорошего способа сделать True и False с нуля, которые наследовались бы от 1 и 0 на чистом Python).Реализация operator.truth():
def truth(obj: Any, /) -> bool:
"""Return True if the object is true, False otherwise.
Analogous to calling bool().
"""
if obj is True:
return True
elif obj is False:
return False
elif obj is None:
return False
obj_type = type(obj)
try:
__bool__ = debuiltins._mro_getattr(obj_type, "__bool__")
except AttributeError:
# Only try calling len() if it makes sense.
try:
__len__ = debuiltins._mro_getattr(obj_type, "__len__")
except AttributeError:
# If all else fails...
return True
else:
return True if debuiltins.len(obj) > 0 else False
else:
boolean = __bool__(obj)
if isinstance(boolean, bool):
# Coerce into True or False.
return truth(boolean)
else:
raise TypeError(
f"expected a 'bool' from {obj_type.__name__}.__bool__(), "
f"not {type(boolean).__name__!r}"
)
Реализация notС реализованным оператором operator.truth(), реализовать operator.not_() – дело всего одной лямбды:
lambda a, /: False if truth(a) else True
Итоговый результат прост, но, чтобы добраться до него, нужно было проделать немало работы.Как обычно, код из этой статьи можно найти в моем проекте desugar.
Узнать подробнее о курсе «Python Developer. Professional».Смотреть открытый вебинар на тему «Визуализация данных с помощью matplotlib».
===========
Источник:
habr.com
===========
===========
Автор оригинала: BRETT CANNON
===========Похожие новости:
- [Программирование, .NET, ASP, C#] Что из себя представляет класс Startup и Program.cs в ASP.NET Core (перевод)
- [Программирование, Функциональное программирование] Сильные стороны функционального программирования
- [Тестирование IT-систем, Программирование, Тестирование веб-сервисов, Карьера в IT-индустрии] Перспективы разработчика в автоматизации тестирования ПО
- [Программирование, Управление разработкой, Управление проектами, Учебный процесс в IT, Изучение языков] Как прорешать SICP: Отчёт о создании решебника для самого известного в мире задачника по программированию (перевод)
- [Программирование, Java] Односторонние и двусторонние отношения в Hibernate
- [Python, Программирование] Как перестать беспокоиться и начать жить
- [Python, Программирование] 7 полезных расширений VS Code для Python-разработчиков (перевод)
- [Программирование, Assembler] Перевод числа в строку с помощью SIMD + FPU
- [Open source] Google захывтывает Python
- [Программирование, Kotlin, Старое железо] Пиксели, Excel, Kotlin и немного ностальгии…
Теги для поиска: #_python, #_programmirovanie (Программирование), #_sintaksicheskij_sahar (синтаксический сахар), #_python, #_matplotlib, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_python, #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:10
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет, Хабр. В преддверии старта курса «Python Developer. Professional» подготовили традиционный перевод полезного материала.
Также приглашаем всех желающих посетить открытый вебинар на тему «Визуализация данных с помощью matplotlib». В этой статье из серии про синтаксический сахар в Python я займусь на первый взгляд очень простым синтаксисом, но на самом деле, чтобы разобраться в механике его работы, нужно погрузиться вглубь на несколько слоев. Мы будем говорить о not.Определение звучит на первый взгляд очень просто:Оператор not выдает True, если его аргумент False, и False в противоположном случае.Достаточно просто, не так ли? Но когда вы начинаете разбираться в том, что считать «истинным» или «ложным», вы быстро понимаете, что есть приличное количество вещей, подходящих под эти определения.(Как и в других статьях этой серии, код на С предназначен для тех, кто хочет пройти путь «по хлебным крошкам», но вы можете пропустить его, если хотите)Реализация notЕсли вы посмотрите на байткод, то заметите единственный опкод, относящийся к not – это UNARY_NOT. Байткод для not a: >>> import dis
>>> def spam(): not a ... >>> dis.dis(spam) 1 0 LOAD_GLOBAL 0 (a) 2 UNARY_NOT 4 POP_TOP 6 LOAD_CONST 0 (None) 8 RETURN_VALUE case TARGET(UNARY_NOT): {
PyObject *value = TOP(); int err = PyObject_IsTrue(value); Py_DECREF(value); if (err == 0) { Py_INCREF(Py_True); SET_TOP(Py_True); DISPATCH(); } else if (err > 0) { Py_INCREF(Py_False); SET_TOP(Py_False); DISPATCH(); } STACK_SHRINK(1); goto error; } /* Test a value used as condition, e.g., in a for or if statement.
Return -1 if an error occurred */ int PyObject_IsTrue(PyObject *v) { Py_ssize_t res; if (v == Py_True) return 1; if (v == Py_False) return 0; if (v == Py_None) return 0; else if (v->ob_type->tp_as_number != NULL && v->ob_type->tp_as_number->nb_bool != NULL) res = (*v->ob_type->tp_as_number->nb_bool)(v); else if (v->ob_type->tp_as_mapping != NULL && v->ob_type->tp_as_mapping->mp_length != NULL) res = (*v->ob_type->tp_as_mapping->mp_length)(v); else if (v->ob_type->tp_as_sequence != NULL && v->ob_type->tp_as_sequence->sq_length != NULL) res = (*v->ob_type->tp_as_sequence->sq_length)(v); else return 1; /* if it is negative, it should be either -1 or -2 */ return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int); }
def index(obj: Object, /) -> int:
"""Losslessly convert an object to an integer object. If obj is an instance of int, return it directly. Otherwise call __index__() and require it be a direct instance of int (raising TypeError if it isn't). """ # https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L1260-L1302 if isinstance(obj, int): return obj length_type = builtins.type(obj) try: __index__ = _mro_getattr(length_type, "__index__") except AttributeError: msg = ( f"{length_type!r} cannot be interpreted as an integer " "(must be either a subclass of 'int' or have an __index__() method)" ) raise TypeError(msg) index = __index__(obj) # Returning a subclass of int is deprecated in CPython. if index.__class__ is int: return index else: raise TypeError( f"the __index__() method of {length_type!r} returned an object of " f"type {builtins.type(index).__name__!r}, not 'int'" ) def len(obj: Object, /) -> int:
"""Return the number of items in a container.""" # https://github.com/python/cpython/blob/v3.8.3/Python/bltinmodule.c#L1536-L1557 # https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L45-L63 # https://github.com/python/cpython/blob/v3.8.3/Objects/typeobject.c#L6184-L6209 type_ = builtins.type(obj) try: __len__ = _mro_getattr(type_, "__len__") except AttributeError: raise TypeError(f"type {type!r} does not have a __len__() method") length = __len__(obj) # Due to len() using PyObject_Size() (which returns Py_ssize_t), # the returned value is always a direct instance of int via # PyLong_FromSsize_t(). index = int(_index(length)) if index < 0: raise ValueError("__len__() should return >= 0") else: return index def truth(obj: Any, /) -> bool:
"""Return True if the object is true, False otherwise. Analogous to calling bool(). """ if obj is True: return True elif obj is False: return False elif obj is None: return False obj_type = type(obj) try: __bool__ = debuiltins._mro_getattr(obj_type, "__bool__") except AttributeError: # Only try calling len() if it makes sense. try: __len__ = debuiltins._mro_getattr(obj_type, "__len__") except AttributeError: # If all else fails... return True else: return True if debuiltins.len(obj) > 0 else False else: boolean = __bool__(obj) if isinstance(boolean, bool): # Coerce into True or False. return truth(boolean) else: raise TypeError( f"expected a 'bool' from {obj_type.__name__}.__bool__(), " f"not {type(boolean).__name__!r}" ) lambda a, /: False if truth(a) else True
Узнать подробнее о курсе «Python Developer. Professional».Смотреть открытый вебинар на тему «Визуализация данных с помощью matplotlib».
=========== Источник: habr.com =========== =========== Автор оригинала: BRETT CANNON ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_python, #_programmirovanie ( Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:10
Часовой пояс: UTC + 5