Дескриптор функции для встроенных типов, таких как dict.fromkeys, ведет себя иначе, чем обычный метод - PullRequest
0 голосов
/ 02 сентября 2018

Почему дескриптор функции для dict.fromkeys работает иначе, чем другие нормальные функции.

Прежде всего, вы не можете получить доступ к __get__ следующим образом: dict.fromkeys.__get__ вы должны получить его из __dict__. (dict.__dict__['fromkeys'].__get__)

И тогда она не будет работать как любая другая функция, потому что она только позволит себе привязаться к dict.

Это отлично работает, что я ожидал:

class Thing:
    def __init__(self):
        self.v = 5

    def test(self):
        print self.v


class OtherThing:
    def __init__(self):
        self.v = 6

print Thing.test
Thing.test.__get__(OtherThing())

однако это делает что-то неожиданное:

#unbound method fromkeys
func = dict.__dict__["fromkeys"]

но ее описание отличается от обычной несвязанной функции следующим образом: <method 'fromkeys' of 'dict' objects> вместо: <unbound method dict.fromkeys>, как и я

это работает как ожидалось:

func.__get__({})([1,2,3])

но вы не можете связать это с чем-то другим, я понимаю, что это не сработает, но это обычно не останавливает нас:

func.__get__([])([1,2,3])

Это происходит с ошибкой типа из дескриптора функции ...:

descriptor 'fromkeys' for type 'dict' doesn't apply to type 'list'

Почему Python различает встроенные функции и обычные функции, подобные этой? И можем ли мы сделать это тоже? Можем ли мы создать функцию, которая будет привязана только к тому типу, которому она принадлежит?

1 Ответ

0 голосов
/ 02 сентября 2018

class_or_type.descriptor всегда вызывает descriptor.__get__(None, class_or_type), поэтому вы не можете получить __get__ от встроенного объекта несвязанного метода. (Unbound) тип method, созданный для __get__ в функции Python 2, явно реализует __get__, потому что методы Python гораздо более гибкие. Таким образом, и builtin.descriptor, и class.descriptor вызывают .__get__(), но тип возвращаемого объекта различен, а для методов Python доступ к атрибуту прокси типа объекта:

Доступ к атрибутам через __dict__, с другой стороны, никогда не вызывает __get__, поэтому class_or_type.__dict__['fromkeys'] дает вам исходный объект дескриптора.

Под капотом такие типы, как dict, включая их методы, реализованы в коде C, не в коде Python , и C гораздо более требователен к типам. Вы никогда не сможете использовать функции, предназначенные для работы с внутренними подробностями реализации dict объекта со списком, так зачем беспокоиться?

Это действительно все, что нужно сделать; методы для встроенных типов не совпадают с функциями / методами, реализованными в Python, поэтому, хотя они в основном работают одинаково, они не могут работать с динамическими типами, и реализация того, как работают дескрипторы, отражает это.

А так как функции Python (обернутые в методы или нет) могут работать с любым типом Python (если таковой спроектирован), несвязанные method объекты поддерживают __get__, так что вы можете взять любой такой объект и прикрепить его к другому классу ; поддерживая __get__, они поддерживают повторную привязку к новому классу.

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


Обратите внимание, что в Python 3 function.__get__(None, classobject) просто возвращает сам объект функции, а не объект несвязанного метода, как это делает Python 2. Полное различие между несвязанным / связанным методом, когда объекты несвязанного метода ограничивают тип для первого аргумента при вызове, оказалось не таким уж полезным, поэтому оно было отброшено.

...