Свойства и методы — Основы Python
У данных, с которыми мы работаем в программировании, обычно есть важные атрибуты и методы. Они определяют поведение и возможности этих данных. Подробнее о них можно узнать в отдельных курсах, которые посвящены объектно-ориентированному программированию в Python. А в этом уроке мы рассмотрим основы работы с методами в Python.
Атрибуты и методы
Атрибуты (attributes) — это переменные, которые хранят данные в объектах классов в Python. У каждого объекта класса есть свой набор атрибутов, которые могут быть доступны для чтения и записи.
Методы (methods) — это функции, которые связаны с определенными объектами данных.
Атрибуты и методы являются такими же выражениями, как переменные и вызовы функций. Также они могут использоваться в комбинации с другими выражениями.
Объекты
В программировании мы оперируем данными, создаем числа и строки, выполняем над ними различные операции и используем полученный результат. Чтобы выполнить операцию, мы применяем либо операторы, либо функции:
В примере выше есть четкое разделение: данные отдельно, функции отдельно. Но это не единственный способ организации кода. В Python наравне с таким разделением используется и другой подход — объектно-ориентированный (ОО).
Объектно-ориентированный код строится на объединении данных и функций в одну сущность — объект. Данные в таком случае называются атрибутами, а функции — методами.
Так это выглядит:
Строки в Python — это объекты. В примере выше мы вызываем метод, то есть функцию, которая связана со строкой. Вызов происходит через точку, которая идет сразу за именем переменной. В остальном методы работают как обычные функции.
Также вызов можно делать и напрямую:
В строки встроено много методов, которые постоянно нужны разработчику. Посмотреть их список можно в документации. Вот несколько полезных примеров:
То же самое касается чисел и остальных типов данных, которые мы пока не изучали. Можно сказать, что в Python почти всё — объекты:
В примере выше есть имя метода, в начале и конце которого по два подчеркивания. В Python так называют методы, которые не предназначены для прямого вызова. Для них создали функции, которые внутри себя уже сами вызывают методы:
Создатель Python решил, что будет нагляднее, если математические или похожие на математические операции выразить функциями. Он хотел, чтобы такие функции воспринимались как операции, типа сложения или вычитания. Так привычнее для тех, кто изучал математику.
Так же работает и функция len() :
Кроме методов у объектов есть атрибуты, но у встроенных в Python объектов их мало. Например, атрибут __doc__ , который возвращает документацию функции. Поэтому функции тоже считаются объектами:
Атрибуты работают и выглядят как переменные, только указываются через точку после объекта.
Теперь поговорим про неизменяемость типов данных.
Неизменяемость
Допустим, у нас есть такой вызов:
Вызов метода .upper() возвращает новое значение, в котором все буквы преобразованы в верхний регистр, но он не меняет исходную строку. Поэтому внутри переменной окажется старое значение: 'Tirion' . Эта логика справедлива для методов всех примитивных типов.
Вместо того, чтобы изменять значение, можно заменить значение. Для этого понадобятся переменные:
Далее поговорим про методы.
Методы как выражения
Методы — такие же выражения, как переменные и вызовы функции, значит, их можно по-разному комбинировать.
Например, использовать в операциях:
Или использовать в аргументах функций:
![]()
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Об обучении на Хекслете
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Урок «Как эффективно учиться на Хекслете»
- Вебинар «Как самостоятельно учиться»
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Атрибуты и протокол дескриптора в Python
Вы, возможно, уже знаете, что у большинства объектов есть внутренний словарь __dict__, содержащий все их аттрибуты. И что особенно радует, как легко можно изучать такие низкоуровневые детали в Питоне:
Давайте начнём с попытки сформулировать такую (неполную) гипотезу:
foo.bar эквивалентно foo.__dict__[‘bar’] .
Пока звучит похоже на правду:

Теперь предположим, что вы уже в курсе, что в классах можно объявлять динамические аттрибуты:
Хм… ну ладно. Видно что __getattr__ может эмулировать доступ к «ненастоящим» атрибутам, но не будет работать, если уже есть объявленная переменная (такая, как foo.bar, возвращающая ‘hello!’, а не ‘goodbye!’). Похоже, всё немного сложнее, чем казалось вначале.
И действительно: существует магический метод, который вызывается всякий раз, когда мы пытаемся получить атрибут, но, как продемонстрировал пример выше, это не __getattr__. Вызываемый метод называется __getattribute__, и мы попробуем понять, как в точности он работает, наблюдая различные ситуации.
Пока что модифицируем нашу гипотезу так:
foo.bar эквивалентно foo.__getattribute__(‘bar’), что примерно работает так:
Проверим практикой, реализовав этот метод (под другим именем) и вызывая его напрямую:
Выглядит корректно, верно?

Отлично, осталось лишь проверить, что поддерживается присвоение переменных, после чего можно расходиться по дом… —
my_getattribute возвращает некий объект. Мы можем изменить его, если он мутабелен, но мы не можем заменить его на другой с помощью оператора присвоения. Что же делать? Ведь если foo.baz это эквивалент вызова функции, как мы можем присвоить новое значение атрибуту в принципе?
Когда мы смотрим на выражение типа foo.bar = 1, происходит что-то больше, чем просто вызов функции для получения значения foo.bar. Похоже, что присвоение значения атрибуту фундаментально отличается от получения значения атрибута. И правда: мы может реализовать __setattr__, чтобы убедиться в этом:
Пара вещей на заметку относительно этого кода:
- __setattr__ не имеет своего аналога __getattribute__ (т.е. магического метода __setattribute__ не существует).
- __setattr__ вызывается внутри __init__, именно поэтому мы вынуждены делать self.__dict__[‘my_dunder_dict’] = <> вместо self.my_dunder_dict = <>. В противном случае мы столкнулись бы с бесконечной рекурсией.

А ведь у нас есть ещё и property (и его друзья). Декоратор, который позволяет методам выступать в роли атрибутов.
Давайте постараемся понять, как это происходит.
Просто ради интереса, а что у нас в f.__dict__?
В __dict__ нет ключа bar, но __getattr__ почему-то не вызывается. WAT?
bar — метод, да ещё и принимающий в качестве параметра self, вот только это метод находится в классе, а не в экземпляре класса. И в этом легко убедиться:
Ключ bar действительно находится в словаре атрибутов класса. Чтобы понять работу __getattribute__, нам нужно ответить на вопрос: чей __getattribute__ вызывается раньше — класса или экземпляра?
Видно, что первым делом проверка идёт в __dict__ класса, т.е. у него приоритет перед экземпляром.
Погодите-ка, а когда мы вызывали метод bar? Я имею в виду, что наш псевдокод для __getattribute__ никогда не вызывает объект. Что же происходит?
descr.__get__(self, obj, type=None) -> value
descr.__set__(self, obj, value) -> None
descr.__delete__(self, obj) -> None
Вся суть тут. Реализуйте любой из этих трёх методов, чтобы объект стал дескриптором и мог менять дефолтное поведение, когда с ним работают как с атрибутом.
Если объект объявляет и __get__(), и __set__(), то его называют дескриптором данных («data descriptors»). Дескрипторы реализующие лишь __get__() называются дескрипторами без данных («non-data descriptors»).
Оба вида дескрипторов отличаются тем, как происходит перезапись элементов словаря атрибутов объекта. Если словарь содержит ключ с тем же именем, что и у дескриптора данных, то дескриптор данных имеет приоритет (т.е. вызывается __set__()). Если словарь содержит ключ с тем же именем, что у дескриптора без данных, то приоритет имеет словарь (т.е. перезаписывается элемент словаря).
Чтобы создать дескриптор данных доступный только для чтения, объявите и __get__(), и __set__(), где __set__() кидает AttributeError при вызове. Реализации такого __set__() достаточно для создания дескриптора данных.
Короче говоря, если вы объявили любой из этих методов — __get__, __set__ или __delete__, вы реализовали поддержку протокола дескриптора. А это именно то, чем занимается декоратор property: он объявляет доступный только для чтения дескриптор, который будет вызываться в __getattribute__.
Последнее изменение нашей реализации:
foo.bar эквивалентно foo.__getattribute__(‘bar’), что примерно работает так:
Попробуем продемонстрировать на практике:

Мы лишь немного поскребли поверхность реализации атрибутов в Python. Хотя наша последняя попытка эмулировать foo.bar в целом корректна, учтите, что всегда могут найтись небольшие детали, реализованные по-другому.
Надеюсь, что помимо знаний о том, как работают атрибуты, мне так же удалось передать красоту языка, который поощряет вас к экспериментам. Погасите часть долга знаний сегодня.
Attributes in Python
Any object-oriented programming language has characteristic properties and behaviour. Characteristic properties in any language are the same as attributes in python. All the attributes of a python class can be excess with the function dir() and vars() .
Program:
Explanation:
Here, we create variable x as a string which is a class attribute and a y string which is an instance attribute. At last print the values of both the variables.

Types of attributes:
Attributes follow two types:
Class attributes:
Attributes that are defined outside the method in the python class object are known as class attributes. These attributes can be accessed by class and instance objects. They belong to the class.
Syntax:
Program:
Explanation:
Here, we create a variable x next to the class attr. Variable x is a class attribute. Create an inject obj of class attr and by the instance of class print the value of x.

Instance attributes:
Instance attributes are the attributes that are defined inside __init__ method. These attributes can be accessed by an instance of the class. They belong to instances of objects.
Syntax:
Program:
Explanation:
Here, we create a variable y inside __init__ method .y is an instance attribute. Create an obj object f class and print the value of y.

Uses of class attributes:
Class attributes are used in many cases. Some of the use cases areas:
- To create a constant value variable
- Listing object/data across all instances
- To give a default value
Class attributes are initialized across all instances of the class. So, class attributes can be used to create constant value variables in the class. These variables can be used in the method. So, attributes take low space in memory and less time to initialize.
Program:
Explanation:
Here, we create a variable pi which takes a constant value. Pi is a class attribute. Create a method area that calculates the area. Create an object of the class and print the area of a circle.

In case we want to excess some properties of all the instances of the class. It is very time-consuming to excess properties by calling each object. So, All the instances can be arranged in a single variable by the use of class attributes.
Program:
Explanation:
Here, we create a list of ids as class attributes which are used inside the init method to append all ids of students inside the list of all the instances of the class. We create three objects for the class. At last print the list.

With class attributes, a default value can be provided to attribute in the custom class. By excessing the attributes by class or instance value can be changed.
Program:
Explanation:
Here, we create a class attribute age which is used inside the votting method to compare the age of instance of the class.

Class to instance attribute:
- A class attribute can be changed to an instance attribute by changing the value of the class attribute with the instance of the class.
- This is not in the case where a class attribute is a mutable object such as a list etc.
Program:
Explanation:
Here we create a variable x as class attributes. The value of x is changed by the obj (instance of the class). Print the value of x by obj, class pro and another object obj2. Check the attributes of obj by printing the dir of obj.

Namespaces in attributes:
- A namespace is a dictionary in python that stores keys as objects of class and values as attributes. A namespace is divided in this case into two parts object namespace and class namespace.
- When a class instance accesses any attribute, it first searches into the object namespace then the class namespace. Instance attributes take preference over class attributes. In programming cost of accessing class attributes by the instance of the class will be time-consuming.
- When a class accesses any attribute, it is searched in the class attribute and otherwise throws an error.
Function and attributes:
- In python, functions are treated as attributes of the class. All the static, class, instance methods are attributes that belong to the class.
- Static and class methods are associated with class whereas the instance method is associated with the instance of the class.
- In python instance methods can also be invoked by the class itself by passing instances of an object in the method. It saves memory in the python program.
Program:
Explanation:
Here, we create three functions funclmethod , funstmethod , funinstmethod with decorate @classmethod, @staticmethod.Create an object of class pro as obj and call the instance method funinstmethod.Secondly , Call the instance method by passing the obj instance of class.

Properties vs. Attributes:
In python, properties are completely different from attributes. Properties are the method with @properties decorator. These are used to access private attributes of a class and act as getter-setter functions in a python class.
Program:
Explanation:
Here, we create methods fun with decorator @property , @fun.setter which are used to get and set the values of variable x.

Conclusion:
We hope this article has given you all a clear idea about the Attributes, types of attributes (class attribute and instance attribute), use of class attributes, class to instance attribute, the namespace in attributes, functions, and attributes, properties vs. attributes.
For object-oriented programmers in Python, dealing with class attributes is essential to know. Therefore, it is highly recommended to use python attributes while programming because it consumes less memory and followed an object-oriented programming approach.
Understanding Attributes, Dicts and Slots in Python
This is a complete definition of a class in Python. Granted, it does nothing, but it’s still valid.
At any later point in time, we can «patch» attributes to our class like this:
Exit fullscreen mode
The class has this new class_attribute value from that point on.
If we instantiate this class with my_object = MyClass() we can verify that the class_attribute value is 42:
Exit fullscreen mode
Of course, we can add attributes to our instances as well:
Exit fullscreen mode
Have you ever wondered where these attributes are stored?
Explicit is better than implicit.
(from the Zen of Python)
Python wouldn’t be Python without a well-defined and customizable behaviour for attributes. The attributes of a «thing» in Python are stored in a magic attribute called __dict__ . We can access it like so:
Exit fullscreen mode
As you can see, the class_attribute is stored in the __dict__ of MyClass itself, whereas the instance_attribute is stored within the __dict__ of my_object .
That means, whenever you access my_object.instance_attribute Python will first look in my_object.__dict__ , and then in MyClass.__dict__ . If the attribute instance_attribute is found in neither dictionary, an AttributeError is raised.
Side Note
What is a «thing» in Python? You see that every «thing» in Python has a __dict__ attribute, even a class itself. Logically, a class like MyClass is of type class , meaning that the class itself is an object of type class . Since this might sound confusing, I use the colloquial term «thing» instead.
«Hacking» the __dict__ attribute
Like always in Python, the __dict__ attribute behaves like any other attribute in Python. Since Python is a language that prefers passing by reference, we can look at a bug that occurs quite frequently by accident. Consider a class AddressBook :
Exit fullscreen mode
Now, let’s create some address books and create some addresses:
Exit fullscreen mode
Interestingly, Alice and Bob now share one address book:
Exit fullscreen mode
This is because the addresses attribute is defined at the class level. The empty list is created only once ( addresses = [] ), namely, when the Python interpreter creates the class. Thus, for any subsequent instance of the AddressBook class, the same list is referenced by addresses . We can fix this bug by moving the creation of the empty list to the instance level like so:
Exit fullscreen mode
By moving the creation of the empty list to the constructor ( __init__ method), a new list is created whenever a new instance of AddressBook is created. Therefore, the instances do not unintentionally share the same list anymore.
Introducing the Borg
Can we leverage this behaviour somehow intentionally? Is there a use case where we want all instances to share the same storage? Turns out there is! There is a Design Pattern called Singleton. This ensures that there is only one instance of the class during the program’s runtime. For example, it can be useful if this is used for a database connection class or a configuration store.
Note that you should use singleton classes only occasionally because they introduce some global state in your program, which makes it hard to test individual components of your program in isolation.
What would be a Pythonic way to implement a singleton-ish pattern?
Consider this class:
Exit fullscreen mode
This class has a _shared attribute initialized as an empty array. We know from the previous paragraphs that the dict instance is the same object for the class.
Inside the constructor ( __init__ ) then, we set the __dict__ of the instance to this shared dictionary. As a result, all dynamically added attributes are shared amongst each instance of that class.
Exit fullscreen mode
Why can’t we set the __dict__ = <> to the class directly like so
Exit fullscreen mode
Exit fullscreen mode
This is because in the latter case, we set the __dict__ attribute to the class itself. However, we access the attribute of the instance by typing borg_2.value . Only when the __dict__ attribute is set on the instance level we can make use of our Borg pattern. The way to achieve this is by using the constructor to change the __dict__ attribute on the instance level.
Memory usage of Attributes
Dynamically adding attributes at runtime on instance level or class level comes with a cost. The dictionary structure is quite memory intensive in Python’s internals. In situations where you instantiate a lot (thousands) of instances, this might become a bottleneck.
However, first things first: What are slots? While you can dynamically add attributes to «things» in Python, slots restrict this functionality. When you add a __slots__ attribute to a class , you pre-define which member attributes you allow. Let’s have a look:
Exit fullscreen mode
With this definition, any instance of SlottedClass can only access the attribute value . Accessing other (dynamic) attributes will raise an AttributeError :
Exit fullscreen mode
Restricting the ability to add attributes dynamically is useful for reducing runtime errors that might occur because of typos in attribute names. Still, more importantly, this restriction will reduce the memory usage of your code – in some cases significantly. Let’s try to check this.
We create two classes, one slotted and one unslotted one. Both classes access an attribute called value inside their __init__ method, and in the case of the slotted class, that is the only attribute in __slots__ .
We create a million instances for each class and store these instances in a list. After that, we look at the list’s size. The list of slotted class instances should be smaller.
Exit fullscreen mode
However, we get back a value of 8448728 for each list. So how do we save memory then using slots?
Let’s use the ipython-memory-usage module to check how much memory is consumed during the runtime of our test programme.
Exit fullscreen mode
As you can see, the slotted version took only roughly 85 MiB of RAM, while the unslotted version needed more than 200 MiB, although the resulting size of the lists is the same.
The reason for this is the way Python handles dict s internally. When not specifying __slots__ , Python uses a dictionary by default to store attributes. This dictionary is dynamic in nature, can be resized, needs to be organized by keys, etc. That’s why Python needs a lot of memory to manage the dictionary.
In the slotted version of the class, the key features of a dict are no longer needed as there is no dynamic resizing allowed anymore. Thus, Python allocates memory upfront for the attributes mentioned in the __slots__ .