Méthode Python-type de wrapper?

Quel est le type de Method-wrapper dans Python 3? Si je définis une classe comme ceci:

class Foo(object):
    def __init__(self, val):
        self.val = val
    def __eq__(self, other):
        return self.val == other.val

Et puis faites:

Foo(42).__eq__

Je reçois:

<bound method Foo.__eq__ of <__main__.Foo object at 0x10121d0>>

, Mais si je le fais (en Python 3 ):

Foo(42).__ne__

Je reçois:

<method-wrapper '__ne__' of Foo object at 0x1073e50>

Qu'est Ce qu'une "méthode d'emballage" type?

Edit: désolé pour être plus précis: class method-wrapper est le type de __ne__, comme si je n':

>>> type(Foo(42).__ne__)
<class 'method-wrapper'>

Alors que le type de __eq__ est:

>>> type(Foo(42).__eq__)
<class 'method'>

En Outre, method-wrapper semble être le type de tout undefined méthode magique sur une classe (donc __le__, __repr__, __str__ etc s'il n'est pas explicitement défini aura également ce type).

Ce qui m'intéresse, c'est comment le method-wrapper classe est utilisée par Python. Toutes les "implémentations par défaut" des méthodes sur une classe ne sont-elles que des instances de ce type?

26
demandé sur Adam Parkin 2012-05-01 21:53:01

2 réponses

C'est parce que les 'méthodes non liées' n'existent pas dans Python 3.

Dans Python 3000, le concept de méthodes non liées a été supprimé, et l'expression "A. spam" renvoie un objet de fonction simple. Il s'est avéré que la restriction selon laquelle le premier argument devait être une instance de A était rarement utile pour diagnostiquer des problèmes, et souvent un obstacle aux usages avancés --- certains l'ont appelé "Duck typing self", ce qui semble un nom approprié. (Source)

En Python 2.x, nous avions des méthodes liées et des méthodes non liées. Une méthode liée était liée à un objet, ce qui signifie que lorsqu'elle était appelée, elle passait l'instance de l'objet en tant que première variable (self, normalement). Une méthode non liée était celle où la fonction était une méthode, mais sans instance à laquelle elle appartenait - elle déclencherait une erreur si quelque chose d'autre qu'une instance d'objet était passé à la méthode.

Maintenant, dans 3.x, ce qui a été changé. Plutôt méthodes liées / non liées, lorsque vous demandez une méthode d'un objet, elle renvoie l'objet fonction, mais enveloppée dans une fonction wrapper qui transmet la variable d'instance. De cette façon, il n'est pas nécessaire de faire une distinction entre les méthodes liées et non liées - les méthodes liées sont encapsulées, non liées.

Pour clarifier la différence:

2.x:

a = A()
f = A.method # f is an unbound method - you must pass an instance of `A` in to it as the first argument.
f = a.method # f is a bound method, bound to the instance `a`.

3.x:

a = A()
f = A.method # f is a function
f = a.method # f is a wrapped function with it's first argument filled with `a`.

a.method peut être considéré comme:

def method-wrapper():
    A.method(a)

Pour en savoir plus, consultez le blog de Guido - l'histoire de Python .

Modifier:

Donc, la raison pour laquelle tout cela s'applique est que, ici, __ne__() n'a pas été remplacé - il est à un état par défaut, qui est un contrôle d'identité, implémenté dans C (ligne 980ish). Le wrapper est là pour fournir à la méthode la fonctionnalité ci-dessus.

20
répondu Gareth Latty 2012-05-03 16:22:18

Il semble que le type <method-wrapper ..> soit utilisé par CPython pour les méthodes implémentées en code C. Fondamentalement, le type n'enveloppe pas une autre méthode. Au lieu de cela, il encapsule une fonction implémentée en C en tant que méthode liée. De cette façon <method-wrapper> est exactement comme un <bound-method> sauf qu'il est implémenté en C.

Dans CPython, il y a deux types spéciaux liés à cela.

  • <slot wrapper> qui (au moins) enveloppe une fonction C implémentée. Se comporte comme un <unbound method> en Disponible 2 (au moins parfois) ou un <function> dans Disponible 3
  • {[2] } qui enveloppe une fonction implémentée en C en tant que méthode liée. Les Instances de ce type ont un attribut __self__ _ qui est utilisé comme premier argument lorsqu'il est appelé.

Si vous avez un <slot wrapper> vous lier à un objet avec __get__ pour obtenir <method-wrapper>:

# returns a <slot_wrapper> on both CPython 2 and 3
sw = object.__getattribute__  

# returns a <method-wrapper>
bound_method = sw.__get__(object()) 

# In this case raises AttributeError since no "some_attribute" exists.
bound_method("some_attribute")  

, Vous pouvez appeler __get__ sur n'importe quelle fonction-comme l'objet en Python pour obtenir un <bound method> ou <method-wrapper>. Note __get__ sur ces deux types retournera simplement self.

Python 3

Le type object dans CPython 3 a des implémentations C pour __ne__ et __eq__, et l'un des autres opérateurs de comparaison. Ainsi object.__ne__ renvoie un <slot wrapper> pour cet opérateur. De même object().__ne__ renvoie un <method-wrapper> qui peut être utilisé pour comparer cet objet.

Puisque vous n'avez pas défini __ne__ dans votre classe, vous obtenez une méthode liée (comme <method-wrapper>) qui est la fonction implémentée en C par exemple d'object (instances dérivées incluses). Mon pari est que cette fonction C vérifiera si vous en avez défini __eq__, appelez ceci, et puis pas le résultat.

Python 2 (pas demandé mais répondu)

Python 2 se comporte significativement différent ici. Puisque nous avons le concept de méthodes non liées. qui exigent que vous les appeliez avec le type de premier argument approprié, les <slot wrapper> et <method-wrapper> ont un __objclass__ qui est le type dont le premier argument doit être une instance.

Plus: Python 2 n'a pas d'implémentations d'opérateurs de comparaisons pour le type object. Ainsi object.__ne__ n'est pas un fonction pour comparer des objets. Plutôt intéressant, le type {[31] } qui est la métaclasse de object a un opérateur __ne__ implémenté en C. Ainsi, vous obtenez une méthode liée de object.__ne__ qui va essayer de comparer le type object avec n'importe quel autre type (ou objet).

Ainsi {[21] } échouera réellement avec un AttributeError puisque object ne définit aucune méthode de ce type. Étant donné que object() == object() fonctionne réellement (donnant False), je suppose que CPython 2 a des cas spéciaux dans l'interpréteur pour la comparaison de objet. Une fois de plus, nous voyons que CPython 3 a nettoyé certains détails d'implémentation moins chanceux de Python 2.

18
répondu driax 2013-11-11 15:46:42