Коробка с питоном
Про __slots__ Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам…
__slots__ и наследование
Важно помнить, что при попытке унаследовать класс с
Из-за этого возникает неоднозначность, какой именно слот использовать в результирующем классе.
#std #slots
Важно помнить, что при попытке унаследовать класс с
__slots__ подкласс их унаследует, но так же и создаст __dict__ для новых атрибутов:class SlotsClass:Это стандартное и понятное поведение. Чтобы избежать создания
__slots__ = ('foo', 'bar')
class ChildSlotsClass(SlotsClass):
pass
>>> obj = ChildSlotsClass()
>>> obj.__slots__
# ('foo', 'bar')
>>> obj.foo = 5
>>> obj.test = 3
>>> obj.__dict__
# {'test': 3}
__dict__, можно снова переопределить __slots__ в подклассе:class SlotsClass:А что с множественным наследованием?
__slots__ = ('foo', 'bar')
class ChildSlotsClass(SlotsClass):
__slots__ = ('baz',)
>>> obj = ChildSlotsClass()
>>> obj.foo = 5
>>> obj.baz = 6
>>> obj.something_new = 3
AttributeError: 'ChildSlotsClass' object has no attribute 'something_new'
class ClassA:Оно не работает. Потому-что каждый класс может иметь свои собственные
__slots__ = ('foo', 'bar',)
class ClassB:
__slots__ = ('baz',)
class C(ClassA, ClassB):
pass
TypeError: multiple bases have instance lay-out conflict
__slots__, которые могут пересекаться с другими классами, а это может привести к тому, что объекты могут быть созданы неправильно или будут иметь непредсказуемое поведение. Из-за этого возникает неоднозначность, какой именно слот использовать в результирующем классе.
#std #slots
🔥5
Решил расширить канал ещё одной тематикой - занимательными задачками.
Пока что буду писать про те, которые встречались на тех. собеседованиях.Они не всегда будут адекватные, но что уж есть :)
А начнём, как полагается с классики. Надо объяснить следующее поведение:
Вопрос в том, что здесь творится с ссылками. Разберём самую первую часть. Пробуем получить id объектов:
На вопрос, почему у них одинаковые идентификаторы ответит деталь реализации PyLong_FromLong(для искушенных читать можно читать отсюда ) , которая указывает, что интерпретатор хранит массив целочисленных объектов для всех чисел в диапазоне от -5 до 256. Поэтому, когда мы создаем переменную с числом в этом диапазоне он просто отдает ссылку на уже существующий объект.
Микрооптимизация, при чём очень важная - так уж получилось что числа из этого диапазона используются чаще всего.
В Java есть похожая оптимизация, там такой диапазон составляет от -128 до 127, но есть нюансы .
Второй вопрос отпадает сам собой (будут разные ссылки), но что будет если мы создадим файл с следующим содержимым и запустим его:
А вот это уже нюанс работы нашего REPL.
Каждая написанная нами строка в нём разбирается отдельно. Но при запуске через файл Python имеет возможность применить дополнительные оптимизации для констант, так как он видит сразу весь код - в этом и различие.
А какие ещё неочевидные моменты вы знаете с REPL или int'ами? Пишите в комменты, обсудим :)
#std #задачки
Пока что буду писать про те, которые встречались на тех. собеседованиях.
А начнём, как полагается с классики. Надо объяснить следующее поведение:
>>> a = 256
>>> b = 256
>>> a is b
True # ???
>>> a = 257
>>> b = 257
>>> a is b
False # ???
Вопрос в том, что здесь творится с ссылками. Разберём самую первую часть. Пробуем получить id объектов:
>>> a = 256
>>> b = 256
>>> id(a), id(b)
(2214170730704, 2214170730704)
На вопрос, почему у них одинаковые идентификаторы ответит деталь реализации PyLong_FromLong
Микрооптимизация, при чём очень важная - так уж получилось что числа из этого диапазона используются чаще всего.
Второй вопрос отпадает сам собой (будут разные ссылки), но что будет если мы создадим файл с следующим содержимым и запустим его:
a = 257
b = 257
print(a is b) # True
А вот это уже нюанс работы нашего REPL.
Каждая написанная нами строка в нём разбирается отдельно. Но при запуске через файл Python имеет возможность применить дополнительные оптимизации для констант, так как он видит сразу весь код - в этом и различие.
А какие ещё неочевидные моменты вы знаете с REPL или int'ами? Пишите в комменты, обсудим :)
#std #задачки
Python documentation
Integer Objects
All integers are implemented as “long” integer objects of arbitrary size. On error, most PyLong_As* APIs return(return type)-1 which cannot be distinguished from a number. Use PyErr_Occurred() to d...
🔥17👏4😢2
Сегодня у нас простенькая задачка, а то пятница, все отдыхать хотят, я понимаю.
Есть следующий код:
Вопрос - что вернется при вызове(ну не просто так же мы собрались, верно?) , но почему?
Ответ, как обычно, есть в документации. Возвращаемое функцией значение определяется последним выполненным return.
Вторым важным аспектом является то, что finally исполняется всегда, поэтому мы и получаем его return.
#std #задачки
Есть следующий код:
def test():
try:
return 1
finally:
return 2
Вопрос - что вернется при вызове
test()? Все и так на этом моменте понимают, что вернётся 2 Ответ, как обычно, есть в документации. Возвращаемое функцией значение определяется последним выполненным return.
Вторым важным аспектом является то, что finally исполняется всегда, поэтому мы и получаем его return.
raise, кстати, тоже работать не будет:def test():
try:
raise ValueError()
finally:
return 3
test() # 3
#std #задачки
Python documentation
8. Errors and Exceptions
Until now error messages haven’t been more than mentioned, but if you have tried out the examples you have probably seen some. There are (at least) two distinguishable kinds of errors: syntax error...
🔥9👏5🤔4🤯1
Там в Python 3.12 добавили нашумевший PEP 659, а у меня пет-проект один давно не обновлялся, и так уж звёзды сошлись, что я сижу второй день обновляю его на 3.12
Задача - есть функционал, который под капотом имеет некоторый класс следующего вида:
Мы определяем новые классы наследуясь от
Ну прямо дженерик напрашивается! Тем более в 3.12 их завезли, красивые:
Встаёт вопрос, а как нам получить наш тип из дженерика?
Для начала, получим
Ещё можно это сделать с помощью
Теперь надо получить получить сам тип в дженерике. В этом нам поможет
Теперь в методе
Проверяем:
Вы восхитительны!
Кстати, эта же штука должна работать ещё вроде как аж с 3.8, так как в нём именно был добавлен
Ага,
#рецепт #std
Задача - есть функционал, который под капотом имеет некоторый класс следующего вида:
class BaseFunction:
serialize_to: None
def serialize(self, data: dict) -> serialize_to:
pass # тут мы используем наш serialize_to
@dataclass
class ModelA:
x: str
class FunctionA(BaseFunction):
serialize_to: ModelA
Мы определяем новые классы наследуясь от
BaseFunction, переопределяем в них serialize_to и вызываем serialize который делает нам инстанс serialize_to. Ну прямо дженерик напрашивается! Тем более в 3.12 их завезли, красивые:
class BaseFunction[T]:
def serialize(self, data: dict) -> T:
pass # тут мы используем наш serialize_to
class FunctionA(BaseFunction[ModelA]):
pass
Встаёт вопрос, а как нам получить наш тип из дженерика?
Для начала, получим
__orig_bases__[0] - он вернёт нам классы, от которых мы наследовались. Так как нам нужен только наш первый класс, мы указываем [0]:>>> FunctionA.__orig_bases__
__main__.BaseFunction[__main__.ModelA]
Ещё можно это сделать с помощью
get_original_bases из types, но его добавили только в 3.12 (почему я об этом сказал - узнаете ниже).Теперь надо получить получить сам тип в дженерике. В этом нам поможет
typing.get_args, который получает все аргументы типа. Дополнительно укажем, что нам нужен первый тип:>>> get_args(FunctionA.__orig_bases__[0])[0]
<class '__main__.ModelA'>
Теперь в методе
serialize класса BaseFunction[T] можно написать штуку, которая автоматически сериализует наши данные:def serialize(self, data: dict) -> T:
type_from_generic = get_args(self.__class__.__orig_bases__[0])[0]
return type_from_generic(**data)
Проверяем:
>>> f = FunctionA()
>>> f.serialize(data={"x": 1})
ModelA(x=1)
Вы восхитительны!
Кстати, эта же штука должна работать ещё вроде как аж с 3.8, так как в нём именно был добавлен
__orig_bases__ (PEP 560), ну и под капотом у новых дженериков используется...>>> FunctionA.__mro__
(<class '__main__.FunctionA'>, <class '__main__.BaseFunction'>, <class 'typing.Generic'>, <class 'object'>)
Ага,
typing.Generic :)#рецепт #std
🔥15❤1