Python property description is strange if you look closely (it's not strange if you think more carefully)

Introduction

When I had a chance to write a description about Python and tried to write a description about properties,

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

"What?" How should I explain this? I thought.

Strange point

So what's wrong? By the way, did Python have an overload?

Yes, the following parts look like you can define the "same name but different number of arguments" method for name (self) and name (self, name).

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

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

But Python can't do that. If you define a method with the same name, it will be overwritten by the later defined one (more precisely, the method name attribute will point to the later defined function object).

Of course, both of the above two methods are used. You can see that each method is executed if you prepare print.

As a side note, I was wondering how to write @ name.setter. Because the name part changes depending on the property.

Karakuri

Decorator

Let's solve the mystery. First, let's review the decorator.

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

The description is almost the same as the following.

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

In other words, the name on the right side of the third line is a function object, but the name after the assignment is completed on the left side is a property object.

I'm curious about how to write it, but let's convert the following description with the same idea.

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

I feel like this.

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

Dout (laughs). The method name is set to name_setter, but this is so that the third line can be executed properly. In fact, if you look at the description of the decorator in the official documentation,

Except that the former code does not temporarily bind the original function to the name func.

It has become. This means that if you use a decorator, the method named name is not defined (the name named named defined by @property is not overwritten).

Summary,

  1. Call the setter method of name (property object)
  2. Set the return value to name again

It means that.

property.setter

Now, if you know what the setter method of the property class is, it's a solution. To find out the answer, we need to step into the Python implementation. The implementation of the property class can be found in Objects / descrobject.c. If you omit the implementation details and paste only the main points,

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

This is the C implementation that corresponds to the setter method of the property class. So, 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;
}

It's a bit long, but to summarize what I'm doing

  1. If get, set, del are not specified in the argument, the original definition is fetched.
  2. Create and return a property object again using the extracted one and the one specified by the argument now.

I am doing that. This means that the returned property method has getters and setters defined.

In addition, about the question "I understand that the property object is set, but how does this work so that it can be referenced and assigned?" [Very excellent article](http: / /qiita.com/knzm/items/a8a0fead6e1706663c22) Please refer to it.

Summary

To summarize the story.

--Question ――The property description looks like an overload, but Python could you do that? --What does @proper name.setter for a setter mean?

Digression

By the way, the explanation of the property that I originally tried to write is

You still don't know Python too much to explain this

I made the tea muddy like this (laughs)

Recommended Posts

Python property description is strange if you look closely (it's not strange if you think more carefully)
If you think the PyCharm environment is broken, it's because of the file name
python> check NoneType or not> if a == None:> if a is None:
When you think the update of ManjaroLinux is strange