Почему в __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))).

Другие вопросы по тегам