La description de la propriété en Python est étrange si vous regardez de près (ce n'est pas étrange si vous réfléchissez plus attentivement)

introduction

J'ai eu la chance d'écrire une description de Python, et quand j'ai essayé d'écrire une description sur les propriétés,

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

"Quoi?" Comment expliquer cela? J'ai pensé.

Point étrange

Alors, qu'est-ce qui ne va pas? Au fait, Python avait-il une surcharge?

Oui, la partie suivante semble que vous pouvez définir la méthode "même nom mais nombre d'arguments différent" pour le nom (soi) et le nom (soi, nom).

@property
def name(self):
    return self._name

@name.setter
def name(self, name):
    self._name = name

Mais vous ne pouvez pas faire cela avec Python. Si vous définissez une méthode avec le même nom, elle sera écrasée par celle définie plus tard (plus précisément, l'attribut du nom de la méthode pointera vers l'objet fonction défini ultérieurement).

Bien entendu, les deux méthodes ci-dessus sont utilisées. Vous pouvez voir que chaque méthode est exécutée si vous préparez l'impression.

Au fait, je me demandais comment écrire @ name.setter. Parce que la partie du nom change en fonction de la propriété.

Karakuri

Décorateur

Résolvons le mystère. Tout d'abord, passons en revue les décorateurs.

@property
def name(self):
    return self._name

La description est presque la même que la suivante.

def name(self):
    return self._name
name = property(name)

En d'autres termes, le nom sur le côté droit de la troisième ligne est un objet fonction, mais le nom une fois l'affectation terminée sur le côté gauche est un objet de propriété.

Je suis curieux de savoir comment l'écrire, mais convertissons la description suivante avec la même idée.

@name.setter
def name(self, name):
    self._name = name

Je me sens comme cela.

def name_setter(self, name):
    self._name = name
name = name.setter(name_setter)

Dout (rires). Le nom de la méthode est défini sur name_setter, mais c'est pour que la troisième ligne puisse être exécutée correctement. En fait, si vous regardez la partie description du décorateur dans le document officiel,

Sauf que l'ancien code ne lie pas temporairement la fonction d'origine au nom func.

Il est devenu. En d'autres termes, lors de l'utilisation d'un décorateur, la méthode nommée name n'est pas définie (le nom nommé name défini par @property n'est pas écrasé).

Résumé,

  1. Appelez la méthode setter de name (propriété objet)
  2. Définissez à nouveau la valeur de retour sur name

Cela signifie que.

property.setter

Maintenant, si vous savez quelle est la méthode setter de la classe de propriété, la solution est. Pour trouver la réponse, nous devons entrer dans l'implémentation Python. L'implémentation de la classe de propriété peut être trouvée dans Objects / descrobject.c. Si vous omettez les détails de l'implémentation et ne collez que les points principaux,

static PyObject *
property_setter(PyObject *self, PyObject *setter)
{
    return property_copy(self, NULL, setter, NULL);
}

Il s'agit de l'implémentation C qui correspond à la méthode setter de la classe de propriété. Donc, property_copy.

static PyObject *
property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del)
{
    propertyobject *pold = (propertyobject *)old;
    PyObject *new, *type, *doc;

    type = PyObject_Type(old);
    if (type == NULL)
        return NULL;

    if (get == NULL || get == Py_None) {
        Py_XDECREF(get);
        get = pold->prop_get ? pold->prop_get : Py_None;
    }
    if (set == NULL || set == Py_None) {
        Py_XDECREF(set);
        set = pold->prop_set ? pold->prop_set : Py_None;
    }
    if (del == NULL || del == Py_None) {
        Py_XDECREF(del);
        del = pold->prop_del ? pold->prop_del : Py_None;
    }
    if (pold->getter_doc && get != Py_None) {
        /* make _init use __doc__ from getter */
        doc = Py_None;
    }
    else {
        doc = pold->prop_doc ? pold->prop_doc : Py_None;
    }

    new =  PyObject_CallFunctionObjArgs(type, get, set, del, doc, NULL);
    Py_DECREF(type);
    if (new == NULL)
        return NULL;
    return new;
}

C'est un peu long, mais pour résumer ce que je fais

  1. Si get, set, del ne sont pas spécifiés dans l'argument, la définition d'origine est récupérée.
  2. Créez et renvoyez un objet de propriété à nouveau en utilisant celui extrait et celui spécifié par l'argument maintenant.

Je fais ça. Cela signifie que la méthode de propriété renvoyée a des getters et des setters définis.

En outre, à propos de la question "Je comprends que l'objet de propriété est défini, mais comment cela fonctionne-t-il pour qu'il puisse être référencé et attribué?" /qiita.com/knzm/items/a8a0fead6e1706663c22) Veuillez vous y référer.

Résumé

Pour résumer l'histoire.

--Question ―― La description de la propriété ressemble à une surcharge, mais Python pourriez-vous le faire?

De côté

À propos, l'explication de la propriété que j'ai initialement essayé d'écrire est

Vous ne connaissez toujours pas trop Python pour expliquer cela

J'ai rendu le thé boueux comme ça (rires)

Recommended Posts

La description de la propriété en Python est étrange si vous regardez de près (ce n'est pas étrange si vous réfléchissez plus attentivement)
Si vous pensez que l'environnement PyCharm est cassé, c'est à cause du nom du fichier
python> vérifier NoneType ou non> si a == None:> si a vaut None:
Quand vous pensez que la mise à jour de ManjaroLinux est étrange