#python python... PYTHON 🔛 🚀
11 subscribers
914 photos
7 videos
158 files
1.54K links
Download Telegram
Решил расширить канал ещё одной тематикой - занимательными задачками.
Пока что буду писать про те, которые встречались на тех. собеседованиях. Они не всегда будут адекватные, но что уж есть :)

А начнём, как полагается с классики. Надо объяснить следующее поведение:

>>> 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 (для искушенных читать можно читать отсюда), которая указывает, что интерпретатор хранит массив целочисленных объектов для всех чисел в диапазоне от -5 до 256. Поэтому, когда мы создаем переменную с числом в этом диапазоне он просто отдает ссылку на уже существующий объект.
Микрооптимизация, при чём очень важная - так уж получилось что числа из этого диапазона используются чаще всего.
В Java есть похожая оптимизация, там такой диапазон составляет от -128 до 127, но есть нюансы.

Второй вопрос отпадает сам собой (будут разные ссылки), но что будет если мы создадим файл с следующим содержимым и запустим его:

a = 257
b = 257
print(a is b) # True


А вот это уже нюанс работы нашего REPL.
Каждая написанная нами строка в нём разбирается отдельно. Но при запуске через файл Python имеет возможность применить дополнительные оптимизации для констант, так как он видит сразу весь код - в этом и различие.

А какие ещё неочевидные моменты вы знаете с REPL или int'ами? Пишите в комменты, обсудим :)

#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 #задачки
Создание временных файлов

В процессе написания скрипта может потребоваться создание временных файлов, которые будут удалены автоматически после завершения работы скрипта или обработки файла.
Это может быть полезно по разным причинам - при обработке больших данных (которые не вместятся в буфер) или при проведении сложных операций (например, можно создать временный файл и натравить на него ffmpeg).

Для решения этих проблем в Python есть модуль tempfile. Нас интересует 2 функции - это TemporaryFile и NamedTemporaryFile.

TemporaryFile позволяет создать безымянный временный файл. Вот так можно создать временный текстовой файл, открыть его на запись и чтение (за это отвечает первый аргумент "w+t", подробнее можно прочитать здесь):

from tempfile import TemporaryFile
with TemporaryFile("w+t") as t:
t.write("Hello, boxwithpython!")
t.seek(0)
data = t.read()

NamedTemporaryFile используется для более продвинутых сценариев, так как он создает файл с именем, поэтому мы можем получить путь к нему и использовать его для дальнейших целей:

from tempfile import 
NamedTemporaryFile
with NamedTemporaryFile("w+t") as t:
t.write("Hello, boxwithpython!")
print(t.name) # /tmp/tmpljhsktjt

#std
Про __slots__

Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам и дополнительные расходы памяти.

Такое поведение нужно не всегда. Бывают случаи, когда мы точно знаем, какие атрибуты будут у наших экземпляров классов. Или же мы хотим ограничить добавление новых атрибутов. Именно для этого и существует __slots__.

Слоты задаются через атрибут __slots__ в классе:

class SlotsClass:
slots = ('foo', 'bar')

>>> obj = SlotsClass()
>>> obj.foo = 5
>>> obj.foo
# 5
>>> obj.another_attribute = 'test'
Traceback (most recent call last):
File "python", line 5, in <module>
AttributeError: 'SlotsClass' object has no attribute 'another_attribute'

Теперь мы не можем добавлять новые атрибуты к нашим объектам. Скорость доступа к атрибутам повышается на 25-30%, потому что при доступе к ним их больше не надо вычислять.
В свою очередь, память экономится из-за того, что у класса не создается __dict__, который как раз хранил атрибуты.

#std #slots
Коробка с питоном
Про __slots__ Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам…
__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