Почему в __get__ люди устанавливают для параметра владельца значение None?
Я видел это довольно часто:
def __get__(self, instance, owner=None):
Почему некоторые люди используют значение по умолчанию None
для owner
параметр?
Это даже сделано в документации по Python:
descr.__get__(self, obj, type=None) --> value
3 ответа
Поскольку владелец может быть легко получен из экземпляра, второй аргумент является необязательным. Аргумент владельца необходим только в том случае, если не существует экземпляра, от которого можно получить владельца.
Это описано в предложении, в котором представлены дескрипторы, PEP 252. Создание типов, похожих на классы:
__get__
: функция, вызываемая одним или двумя аргументами, которая извлекает значение атрибута из объекта. Это также упоминается как операция "связывания", поскольку она может возвращать объект "связанного метода" в случае дескрипторов метода. Первый аргумент,X
, - это объект, из которого атрибут должен быть извлечен или к которому он должен быть привязан. Когда ХNone
необязательный второй аргумент,T
, должен быть мета-объектом, и операция привязки может возвратить несвязанный метод, ограниченный экземплярамиT
,
(Жирный акцент мой).
Связывание, начиная с первого дня, должно было быть применимо к одному экземпляру, причем тип был необязательным. Методы не нуждаются, например, в том, что они могут быть связаны только с экземпляром:
>>> class Foo: pass
...
>>> def bar(self): return self
...
>>> foo = Foo()
>>> foo.bar = bar.__get__(foo) # look ma! no class!
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>>
>>> foo.bar()
<__main__.Foo object at 0x10a0c2710>
Кроме того, второй аргумент может быть легко получен из первого аргумента; свидетель classmethod
все еще привязка к классу, хотя мы не передали один в:
>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>
Единственная причина, по которой аргумент существует в первую очередь, заключается в поддержке привязки к классу, например, когда нет экземпляра для привязки. Метод класса снова; привязка к None
поскольку экземпляр не будет работать, он будет работать, только если мы действительно передадим класс:
>>> classmethod(bar).__get__(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __get__(None, None) is invalid
>>> classmethod(bar).__get__(None, Foo)
<bound method type.bar of <class '__main__.Foo'>>
Это стандартный способ сделать это; все встроенные дескрипторы Python, которые я видел, делают это, включая функции, свойства, статические методы и т. д. Я не знаю ни одного случая в протоколе дескриптора, где __get__
будет вызываться без аргумента владельца, но если вы хотите позвонить __get__
вручную может быть полезно не пропускать владельца. Аргумент владельца обычно ничего не делает.
Например, вам может потребоваться более чистый способ дать отдельным объектам новые методы. Следующий декоратор очищает синтаксис и позволяет методам иметь доступ к self
:
def method_of(instance):
def method_adder(function):
setattr(instance, function.__name__, function.__get__(instance))
return function
return method_adder
@method_of(a)
def foo(self, arg1, arg2):
stuff()
Сейчас a
имеет foo
метод. Мы вручную использовали __get__
метод foo
функция для создания объекта привязанного метода, как и любой другой, за исключением того, что, поскольку этот метод не связан с классом, мы не передали __get__
класс. Практически единственное отличие состоит в том, что при печати объекта метода вы видите ?.foo
вместо SomeClassName.foo
,
Потому что так описывается протокол дескриптора:
descr.__get__(self, obj, type=None) --> value
cf https://docs.python.org/2/howto/descriptor.html
type
Аргумент разрешает доступ к классу, в котором ищется дескриптор, когда он просматривается в классе, а не в экземпляре. Поскольку вы можете получить класс из экземпляра, он как-то избыточен, когда дескриптор ищется в экземпляре, поэтому он сделан необязательным, чтобы позволить менее многословному desc.__get__(obj)
позвонить (вместо desc.__get__(obj, type(obj))
).