Объекты в Python

Содержание
Введение
id()
dir()
Равенство по значению и по идентичности
Изменяемые и неизменяемые объекты
Передача аргументов
Всё является объектом
Размер объекта
Похожие статьи

Введение

Основной объект в Python, от которого всё наследуется это PyObject python.org

У него есть два атрибута: счётчик ссылок Py_REFCNT и тип Py_TYPE .

Про итерацию, итераторы и итерируемые объекты вы можете прочитать в статье «Итерация в Python»

Про типы данных в Python можно прочитать много интересного в официальной документации

id()

Рассмотрим очень полезную для дальнейшего понимания объектом функцию id()

# id() print("# id():\n") a = 1961 print("a = 1961") print(f"id(a) = {id(a)}")

# id(): a = 1961 id(a) = 140042834389712

b = 1065 print("b = 1065") print(f"id(b) = {id(b)}")

b = 1065 id(b) = 140042834389648

b - это новый элемент со своим уникальным id

Если выполнить b = a, то b получит id и значение от a, то есть станет тем же объектом, просто у объекта a появится второе название.

b = a print("b = a") print(f"id(b) = {id(b)}") print(f"id(a) == id(b): {id(a) == id(b)}") print(f"a is b: {a is b}")

b = a id(b) = 140042834389712 id(a) == id(b): True a is b: True

В циклах при увеличении счётчика создаётся новый объект.

i = 0 print("i = 0") print(f"id(i) = {id(i)}") i += 2 print("i += 2") # Создаётся новый объект print(f"id(i) = {id(i)}")

i = 0 id(i) = 140042835400976 i += 2 id(i) = 140042835401008

dir()

Получить все аттрибуты объекта можно с помощью функции dir()

Рассмотрим обычную строку

print("l =", l) print("m =", m)

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

А теперь простой список

l = ["l", "m"] print(dir(l))

['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Видно, что некоторые атрибуты совпадают, а некоторые нет. Например, у строк нет метода copy а у списков upper.

Равенство по значению и по идентичности

== сравнивает значения объектов.

is проверяет являются ли объекты одним и тем же объектом в памяти. То есть сравнивает ссылки на область в памяти.

# Value vs. Identity Equality print("\n# Value vs. Identity Equality:\n") p = [5, 6, 7] print(f"p = {p}") q = [5, 6, 7] print(f"q = {q}") print(f"p == q: {p == q}") print(f"p is q: {p is q}") print(f"p is p: {p is p}") # Value-equality and identity equality are fundamentally # different concepts. # Comparison by value can be controlled programatically # Identity comparison is unalterable defined # by the language # Value vs. Identity Equality:

p = [5, 6, 7] q = [5, 6, 7] p == q: True p is q: False p is p: True

Изменяемые и неизменяемые объекты

Объекты могут быть изменяемыми (mutable) и неизменяемыми (immutable)

Очень хорошей иллюстрацией разницы в их применении являются аргументы по умолчанию

Множество (set) , Словарь (dict) и Список (list) это изменяемые объекты.

Строка - это неизменяемый объект

# Пример 1. Строка a = "aaa" b = a print("a = ", a) print("b = ", b) print("id(a) == id(b):", id(a) == id(b)) # True так как a и b это один и тот же объект print("id(a): ", id(a)) b = "bbb" print("id(a) == id(b):", id(a) == id(b)) # False так как b теперь новый объект print("id(a): ", id(a)) print("id(b): ", id(b)) print("a =", a) print("b =", b)

a = aaa b = aaa id(a) == id(b): True id(a): 2037982796544 id(a) == id(b): False id(a): 2037982796544 id(b): 2037982798368 a = aaa b = bbb

Список - это изменяемый объект, но в этом примере поведение списка и строки пока что одинаковое потому что и
b = "bbb"
и
m = ["m", "m"]
создают новые объекты.

# Пример 1. Список lst = ["l", "l"] m = lst print("lst: ", lst) print("m: ", m) print("id(lst) == id(m): ", id(lst) == id(m)) # True m = ["m", "m"] print("lst = ", lst) print("m = ", m) print("id(lst) == id(m): ", id(lst) == id(m)) # False

lst: ['l', 'l'] m: ['l', 'l'] id(lst) == id(m): True lst = ['l', 'l'] m = ['m', 'm'] id(lst) == id(m): False

А в следующем примере уже видна разница. Изначальная строка a останется без изменений, а вот список l изменится из-за изменения m

# Пример 2. Строка a = "heihei" b = a print("a =", a) print("b =", b) print("id(a) == id(b): ", id(a) == id(b)) # True b += ".ru" print("a =", a) print("b =", b) print("id(a) == id(b): ", id(a) == id(b)) # False # Пример 2. Список lst = ["heihei.ru", "topbicycle.ru"] m = lst print("lst = ", lst) print("m = ", m) print("id(lst) == id(m)", id(lst) == id(m)) # True m.append("beget.com") print("lst =", lst) print("m =", m) print("id(lst) == id(m)", id(lst) == id(m)) # True здесь проявилась изменяемость списка.

a = heihei b = heihei id(a) == id(b): True a = heihei b = heihei.ru id(a) == id(b): False lst = ['heihei.ru', 'topbicycle.ru'] m = ['heihei.ru', 'topbicycle.ru'] id(lst) == id(m) True lst = ['heihei.ru', 'topbicycle.ru', 'beget.com'] m = ['heihei.ru', 'topbicycle.ru', 'beget.com'] id(lst) == id(m) True

С помощью функции id() можно убедиться в том, что строка b стала новым объектом после b += "b" а список m и список l остались одним и тем же объектом.

В следующем примере мы изменим список не с помощью append(), а напрямую, но результат будет таким же.

# Mutable Objects print("\n# Mutable Objects:\n") my_list = [0, 1, 2] print(f"my_list = {my_list}") s = my_list print("s = my_list") print(f"s = {s}") # Изменим элемент в s # и увидим, что и изначальный my_list изменится s[1] = 34 print("s[1] = 34") print(f"s = {s}") # Изменился не только s но и my_list print(f"my_list = {my_list}") print(f"s is my_list: {s is my_list}") # Python doesn't have variables in the sense of boxes # holding a value # Python has named references to objects

python objects_example.py

# Mutable Objects: my_list = [0, 1, 2] s = my_list s = [0, 1, 2] s[1] = 34 s = [0, 34, 2] my_list = [0, 34, 2] s is my_list: True

Передача аргументов

m = [0, 1, 2] print(f"m = {m}") def modify(k): k.append(3) print("k =", k) print("\nmodify(m)\n") modify(m) print(f"m = {m}") print("\nNo copy of m is made\n") print("\nmodify(m)\n") modify(m) print(f"m = {m}")

m = [0, 1, 2] modify(m) k = [0, 1, 2, 3] m = [0, 1, 2, 3] No copy of m is made modify(m) k = [0, 1, 2, 3, 3] m = [0, 1, 2, 3, 3]

Функция modify() работает с тем же объектом, поэтому его значения изменяются. Обойти эту особенность можно с помощью функции copy()

Рассмотрим функцию replace() в которой создается новый объект

f = [4, 5, 6] print("f = ", f) def replace(g): g = [7, 8, 9] print("g =", g) print("\nreplace(f)\n") replace(f) print("f = ", f)

python replace.py

f = [4, 5, 6] replace(f) g = [7, 8, 9] f = [4, 5, 6]

Благодаря тому, что ссылка на старый объект не используется (вместо это переменная g указывает на новый объект) значение переменной f не изменилось.

Можно переписать функцию replace() так, чтобы она заменяла исходный объект.

Назовём новую функцию replace_contents()

def replace_contents(g): g[0] = 20 g[1] = 21 g[2] = 22 print("g = ", g) f = [10, 11, 12] print("f = ", f) print("\nreplace_contents(f)\n") replace_contents(f) print("f = ", f) # Function arguments are transferred using # pass-by-object-reference # References to objects are copied, # not the objects themselves.

python replace_contents.py

f = [10, 11, 12] replace_contents(f) g = [20, 21, 22] f = [20, 21, 22]

Убедиться в том, что если не произведено специальных действий, функция вернёт тот же самый объект довольно просто

def f(d): return d c = [0, 1, 2] print("c = ", c) e = f(c) print("e = f(c)") print(f"c is e: {c is e}")

python return_semantics.py

c = [0, 1, 2] e = f(c) c is e: True

Всё является объектом

Рассмотрим скрипт words.py со следюущим содержанием.

"""Retrieve and print words from a URL. Usage: python3 words.py <URL> """ import sys from urllib.request import urlopen def fetch_words(url): """Fetch a list of words from a URL. Args: url: The URL of a UTF-8 text document. Returns: A list of strings containing the words from the document """ # PEP 257 # story = urlopen("http://sixty-north.com/c/t.txt") story = urlopen(url) story_words = [] for line in story: line_words = line.decode("utf8").split() for word in line_words: story_words.append(word) story.close() return story_words def print_items(story_words): """Print items one per line. Args: An iterable series of printable items. """ for word in story_words: print(word) def main(url): words = fetch_words(url) print_items(words) if __name__ == "__main__": main(sys.argv[1])

➭ python

Python 3.9.5 (default, Jun 15 2021, 15:30:04) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.

>>> import words
>>> type(words)

<class 'module'>

Получить все аттрибуты объекта можно с помощью функции dir()

>>> dir(words)

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fetch_words', 'main', 'print_items', 'sys', 'urlopen']

>>> type(words.fetch_words)

<class 'function'>

>>> dir(words.fetch_words)

['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> words.fetch_words.__name__

'fetch_words'

>>> words.fetch_words.__doc__

'Fetch a list of words from a URL.\n \n Args:\n url: The URL of a UTF-8 text document.\n\n Returns:\n A list of strings containing the words from\n the document\n '

Размер объекта в памяти

Рассмотрим несколько способов изменения размера объекта. Начнём со списка

import pickle from sys import getsizeof from pympler import asizeof l = ["apple", "banana", "cherry"] print(f"getsizeof(l): {getsizeof(l)}") print(f"asizeof(l): {asizeof.asizeof(l)}") print(f"pickle.dumps(l): {len(pickle.dumps(l))}")

getsizeof(l): 80 asizeof(l): 248 pickle.dumps(l): 42

Теперь проверим на строке

s = "applebananacherry" print(f"getsizeof(s): {getsizeof(s)}") print(f"asizeof(s): {asizeof.asizeof(s)}") print(f"pickle.dumps(s): {len(pickle.dumps(s))}")

getsizeof(s): 66 asizeof(s): 72 pickle.dumps(s): 32

Теперь проверим на кастомном классе из статьи про __slots__

import timeit from pympler import asizeof class Developer: __slots__ = ("name", "age", "salary", "framework") def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework class Programmer: def __init__(self, name, age, salary, framework): self.name = name self.age = age self.salary = salary self.framework = framework slotted_emp = Developer("Andrei", 36, 5000, "FastAPI") not_slotted_emp = Programmer("Eugene", 59, 95000, "Spring") print(f"asizeof(s) slotted size: {asizeof.asizeof(slotted_emp)}") print(f"asizeof(s) not slotted size: {asizeof.asizeof(not_slotted_emp)}") print(f"getsize() slotted size: {getsizeof(slotted_emp)}") print(f"getsize() not slotted size: {getsizeof(not_slotted_emp)}") print(f"pickle.dumps() slotted size: {len(pickle.dumps(slotted_emp))}") print(f"pickle.dumps() not slotted size: {len(pickle.dumps(not_slotted_emp))}")

sizeof(s) slotted size: 240 asizeof(s) not slotted size: 560 getsize() slotted size: 64 getsize() not slotted size: 48 pickle.dumps() slotted size: 106 pickle.dumps() not slotted size: 105

Article author: Andrei Olegovich

Похожие статьи
Основы Python
configparser
docstring
#!: Shebang
Объекты
Итерация
os
pathlib

Search this site

Subscribe to Telegram channel @aofeed to follow new articles and updates to old ones

Go to channel

@aofeed

Ask a question in the Telegram group

@aofeedchat

Contacts and cooperation:
I recommend our hosting beget.ru
Write to info@urn.su, info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: