- Python __getattr__ and __getattribute__ magic methods
- __getattr__
- __getattribute__
- A more realistic example
- getattr()
- __getattr__
- __getattribute__
- __ get __
- Conclusion
- Функция getattr() в Python
- Базовый синтаксис getattr()
- Примеры
- Почему следует использовать getattr()?
- Обращения к атрибутам: __getattr__ и __setattr__ в Python
Python __getattr__ and __getattribute__ magic methods
Magic Methods are a broad and general term that refers to “special” methods in a Python class. There is no single definition for all of them, as their use is diverse. For example, a few common and well-known magic methods include:
- __init__ that serves as the object initializer (sometimes incorrectly referred to as constructor)
- __str__ that provides a “string representation” of your object
- __add__ that allows you to “overload” the + operator.
What do all these methods have in common? Well, obviously they all start and end with double underscores ( __ ). But aside from that, what makes them “magic methods” is that they’re invoked somehow “specially”. We don’t manually invoke these methods; Python is the one doing it. For example, we don’t do obj.__str__() , we do str(obj) .
There are many magic methods, but as we will focus on __getattr__ and __getattribute__ in this post.
__getattr__
Let’s start with __getattr__ . This method will allow you to “catch” references to attributes that don’t exist in your object. Let’s see a simple example to understand how it works:
class Dummy(object): pass d = Dummy() d.does_not_exist # Fails with AttributeError
In this example, the attribute access fails (with an AttributeError ) because the attribute does_not_exist doesn’t exist.
But using the __getattr__ magic method, we can intercept that inexistent attribute lookup and do something so it doesn’t fail:
class Dummy(object): def __getattr__(self, attr): return attr.upper() d = Dummy() d.does_not_exist # 'DOES_NOT_EXIST' d.what_about_this_one # 'WHAT_ABOUT_THIS_ONE'
But if the attribute does exist, __getattr__ won’t be invoked:
class Dummy(object): def __getattr__(self, attr): return attr.upper() d = Dummy() d.value = "Python" print(d.value) # "Python"
__getattribute__
__getattribute__ is similar to __getattr__ , with the important difference that __getattribute__ will intercept EVERY attribute lookup, doesn’t matter if the attribute exists or not. Let me show you a simple example:
class Dummy(object): def __getattribute__(self, attr): return 'YOU SEE ME?' d = Dummy() d.value = "Python" print(d.value) # "YOU SEE ME?"
In that example, the d object already has an attribute value . But when we try to access it, we don’t get the original expected value (“Python”); we’re just getting whatever __getattribute__ returned. It means that we’ve virtually lost the value attribute; it has become “unreachable”.
If you ever need to use __getattribute__ to simulate something similar to __getattr__ you’ll have to do some more advanced Python handling:
class Dummy(object): def __getattribute__(self, attr): __dict__ = super(Dummy, self).__getattribute__('__dict__') if attr in __dict__: return super(Dummy, self).__getattribute__(attr) return attr.upper() d = Dummy() d.value = "Python" print(d.value) # "Python" print(d.does_not_exist) # "DOES_NOT_EXIST"
A more realistic example
It’s not common to just randomly catch every attribute lookup. We generally use __getattr__ with a clear objective in mind: whenever we want to provide some dynamic access to our objects. A good example could be extending the base Python tuple to add it some Scala flavor to it. In Scala, tuples are created really similarly to Python:
val a_tuple = ("z", 3, "Python", -1)
But they’re accessed in a different way:
println(a_tuple._1) // “z” println(a_tuple._3) // “Python”
Each element in the tuple is accessed as an attribute, with the first element being the attribute _1 , the second _2 , and so on.
In that example, you can see how we’re catching missing attributes with __getattr__ , but if that attribute is not in the form of _n (where n is an integer), we just raise the AttributeError manually.
We can easily extend our common Python tuple to match this behavior, the code is really simple:
class Tuple(tuple): def __getattr__(self, name): def _int(val): try: return int(val) except ValueError: return False if not name.startswith('_') or not _int(name[1:]): raise AttributeError("'tuple' object has no attribute '%s'" % name) index = _int(name[1:]) - 1 return self[index] t = Tuple(['z', 3, 'Python', -1]) print(t._1) # 'z' print(t._2) # 3 print(t._3) # 'Python' t = Tuple(['z', 3, 'Python', -1]) assert t._1 == 'z' assert t._2 == 3 assert t._3 == 'Python' assert t._4 == -1
getattr()
getattr (object, name[, default]) is one of Python’s built-in functions, its role is to get the properties of the object.
- Object object
- Name attribute name
- Default The default value is returned when the property does not exist
class Foo: def __init__(self, x): self.x = x f = Foo(10) getattr(f, 'x') f.x # 10 getattr(f, 'y', 'bar') # 'bar'
__getattr__
object. __getattr__(self, name) Is an object method that is called if the object’s properties are not found.
This method should return the property value or throw AttributeError .
Note that if the object property can be found through the normal mechanism, it will not be called. __getattr__ method.
class Frob: def __init__(self, bamf): self.bamf = bamf def __getattr__(self, name): return 'Frob does not have `<>` attribute.'.format(str(name)) f = Frob("bamf") f.bar # 'Frob does not have `bar` attribute.' f.bamf # f'bamf'
__getattribute__
This method is called unconditionally when accessing the properties of an object. This method only works for new classes.
The new class is a class that integrates from object or type.
If the class is also defined at the same time __getattr__( ), it will not be called __getattr__() unless __getattribute__() shows the call __getattr__() Or thrown AttributeError . The method should return the property value or throw AttributeError . To avoid infinite recursion in methods, you should always use the methods of the base class to get the properties:
object.__getattribute__(self, name).
grammar: object. __getattribute__(self, name)
class Frob(object): def __getattribute__(self, name): print (f"getting ``") return object.__getattribute__(self, name) f = Frob() f.bamf = 10 f.bamf # getting `bamf`10
__ get __
__get__() The method is one of the descriptor methods. Descriptors are used to transform access object properties into call descriptor methods.
class Descriptor(object): def __get__(self, obj, objtype): print(f"get value=") return self.val def __set__(self, obj, val): self.val = val class Stu(object): age = Descriptor() stu = Stu() stu.age = 12
Conclusion
Magic Methods are a great mechanism to extend the basic features of Python classes and objects and provide more intuitive interfaces. You can provide dynamic attribute lookups with __getattr__ for those missing attributes that you want to intercept. But be careful with __getattribute__ , because it might be tricky to implement correctly without losing attributes in the Python void.
Функция getattr() в Python
Функция getattr() в Python помогает получить атрибут объекта. Это очень похоже на функцию setattr(), но не изменяет объект.
Базовый синтаксис getattr()
Синтаксис функции getattr() следующий:
value = getattr(object, attribute_name[, default_value])
Здесь мы передаем object в getattr() . Он пытается получить attribute_name (которое должно быть строкой) этого объекта. Если атрибут существует, он даст нам соответствующее value .
Это эквивалентно синтаксису:
value = object.attribute_name
В противном случае возможны два случая:
- Если указано значение по умолчанию ( default_value ), он просто будет использовать его и вернет значение этого значения по умолчанию.
- В противном случае он просто вызовет исключение AttributeError Exception, поскольку наш атрибут не найден.
Задача getattr() — не только получить значение атрибута, но и проверить его существование!
Примеры
Давайте сначала рассмотрим простой пример, в котором у вас есть класс Student с атрибутами name и roll_num . Мы получим их с помощью getattr() .
class Student(): def __init__(self, name, roll_no): self.name = name self.roll_no = roll_no student = Student('Amit', 8) print(f"Name:") print(f"Roll No: ")
Действительно, мы установили правильные атрибуты и возвращаем их с помощью getattr() !
В этом случае, поскольку присутствовали оба атрибута, ошибок не было. Однако давайте попробуем получить атрибут, который мы не поместили в класс; например — age .
class Student(): def __init__(self, name, roll_no): self.name = name self.roll_no = roll_no student = Student('Amit', 8) print(f"Name:") print(f"Roll No: ") # Will raise 'AttributeError' Exception since the attribute 'age' is not defined for our instance print(f"Age: ")
Name: Amit Roll No: 8 Traceback (most recent call last): File "getattr_example.py", line 12, in print(f"Age:") AttributeError: 'Student' object has no attribute 'age'
Здесь мы попытались получить атрибут, который не был определен. Поскольку не было параметров по умолчанию, Python напрямую вызвал исключение.
Если мы хотим установить для параметра по умолчанию значение ‘100’ , то в этом случае, хотя мы пытаемся получить age , поскольку его нет, вместо этого он вернет 100 .
class Student(): def __init__(self, name, roll_no): self.name = name self.roll_no = roll_no student = Student('Amit', 8) print(f"Name:") print(f"Roll No: ") # Will not raise AttributeError, since a default value is provided print(f"Age: ")
Name: Amit Roll No: 8 Age: 100
Почему следует использовать getattr()?
Поскольку мы упоминали ранее, что эта функция эквивалентна object.attribute_name , в чем смысл этой функции?
Хотя приведенная выше инструкция с использованием записи верна, она действительна только в том случае, если имя атрибута действительно определено во время вызова.
Итак, если ваши классы таковы, что структура объекта время от времени меняется, вы можете использовать getattr() чтобы проверить, находится ли объект в каком-то конкретном состоянии.
Это также дает нам хороший способ легко обрабатывать исключения, а также предоставлять резервные значения по умолчанию. Если что-то пошло не так, мы могли либо перехватить исключение AttributeError либо проверить значение по умолчанию.
Обращения к атрибутам: __getattr__ и __setattr__ в Python
Метод __getattr__ выполняет операцию получения ссылки на атрибут. Если говорить более определенно, он вызывается с именем атрибута в виде строки всякий раз, когда обнаруживается попытка получить ссылку на неопределенный(несуществующий) атрибут.
Этот метод не вызывается, если интерпретатор может обнаружить атрибут посредством выполнения процедуры поиска в дереве наследования. Вследствие этого метод __getattr__ удобно использовать для обобщенной обработки запросов к атрибутам. Например:
В этом примере класс empty и его экземпляр X не имеют своих собственных атрибутов, поэтому при обращении к атрибуту X.age вызывается метод __getattr__ – в аргументе self передается экземпляр(X), а в аргументе attrname – строка с именем неопределенного атрибута (“age”).
Класс выглядит так, как если бы он действительно имет атрибут age, возвращая результат обращения к имени X.age(40). В результате получается атрибут, вычисляемый динамически.
Для атрибутов, обработка которых классом не предусматривается, метод __getattr__ возбуждает встроенное исключение AttributeError, чтобы сообщить интерпретатору, что это действительно неопределенные имена. Поэтому попытка обращения к имени X.name приводит к появлению ошибки.
Родственник метода __getattr__ является метод перезагрузки __setattr__ который перехватывает все попытки присваивания зачений атрибутам. Если этот метод определен, выражение self.attr = value будет преобразовано в вызов метода self.__setattr__(‘attr’, value).
Как раскрутить Аск фм своими силами и платно, об этом пишет блог pricesmm.com. Если вы пока не в курсе, то узнаете, чем интересна эта сеть и для чего её раскручивать. Как получить лайки Аск фм разными способами, в том числе и на платном сервисе – автор изучил и проверил лично. Знакомьтесь с опытом.
Работать с этим методом сложнее, потому что любая попытка выполнить присваивание любому атрибуту аргумента self приводит к повторному вызову метода __setattr__, вызывая бесконечный цикл рекурсивных вызовов(и, в конечном итоге, исключение переполнения стека!).
Если вам потребуется использовать этот метод, все присваивания в нем придется выполнять посредством словаря атрибутов. Используйте self.__dict__[‘name’] = x, а не self.name = x: