__get __()Python中的方法/函数

 收藏

我有一段我想理解的代码,即使有了现有的答案,我也真的无法理解以下代码的目的,有人可以帮我理解一下吗?

I have already looked a various relevant questions ( __get__() ) here and I couldnt find specific answers. I understand that the class below is trying to create a method on the fly ( possibly we get to this class from a __getattr__() method which fails to find an attribute ) and return the method to the caller. I have commented right above the lines of code I need understanding with.

class MethodGen(object):

    def __getattr__(self, name):
        method = self.method_gen(name)
        if method:
           return self.method_gen(name)


    def method_gen(self, name):

        def method(*args, **kwargs):
            print("Creating a method here")

        # Below are the two lines of code I need help understanding with
        method.__name__ = name
        setattr(self, name, method.__get__(self))

        return method

If I am not wrong, the method() function's attribute __name__ has been set, but in setattr() function, the attribute of the class MethodGen, name is set to what ?

回复
  • wet 回复

    这个问题真的让我很感兴趣。提供的两个答案似乎并不能说明全部问题。困扰我的是这一行:

    setattr(self, name, method.__get__(self))
    

    the code is not setting things up so that method.__get__ Will be called at some point. Rather, method.__get__ is actually Being Called! But isn't the idea that this __get__ method will be called when a particular attribute of an object, an instance of MethodGen in this case, is actually referenced? If you read the docs, this is the impression you get...that an attribute is linked to a Descriptor that implements __get__, and that implementation determines what gets returned when that attribute is referenced. But again, that's not what's going on here. This is all happening before that point. So what IS really going on here?

    The answer lies HERE. The key language is this:

    To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound methods when they are invoked from an object.

    method.__get__(self) is exactly what's being described here. So what method.__get__(self) is actually doing is returning a reference to the "method" function that is bound to self. Since in our case, self is an instance of MethodGen, this call is returning a reference to the "method" function that is bound to an instance of MethodGen. In this case, the __get__ method has nothing to do with the act of referencing an attribute. Rather, this call is turning a function reference into a method reference!

    So now we have a reference to a method we've created on the fly. But how do we set it up so it gets called at the right time, when an attribute with the right name is referenced on the instance it is bound to? That's where the setattr(self, name, X) part comes in. This call takes our new method and binds it to the attribute with name name on our instance.

    上面所有这些就是为什么:

    setattr(self, name, method.__get__(self))
    

    is adding a new method to self, the instance of the MethodGen class on which method_gen has been called.

    The method.__name__ = name part is not all that important. Executing just the line of code discussed above gives you all the behavior you really want. This extra step just attaches a name to our new method so that code that asks for the name of the method, like code that uses introspection to write documentation, will get the right name. It is the instance attribute's name...the name passed to setattr...that really matters, and really "names" the method.

  • rtotam 回复

    考虑下面的类:

    class Foo:
        def bar(self):
            print("hi")
    
    f = Foo()
    f.bar()
    

    bar is a class attribute that has a function as its value. Because function implements the descriptor protocol, however, accessing it as Foo.bar or f.bar does not immediately return the function itself; it causes the function's __get__ method to be invoked, and that returns either the original function (as in Foo.bar) or a new value of type instancemethod (as in f.bar). f.bar() is evaluated as Foo.bar.__get__(f, Foo)().

    method_gen takes the function named method, and attaches an actual method retrieved by calling the function's __get__ method to an object. The intent is so that something like this works:

    >>> m = MethodGen()
    >>> n = MethodGen()
    >>> m.foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MethodGen' object has no attribute 'foo'
    >>> n.foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MethodGen' object has no attribute 'foo'
    >>> m.method_gen('foo')
    <function foo at 0x10465c758>
    >>> m.foo()
    Creating a method here
    >>> n.foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MethodGen' object has no attribute 'foo'
    

    Initially, MethodGen does not have any methods other than method_gen. You can see the exception raised when attempting to invoke a method named foo on either of two instances. Calling method_gen, however, attaches a new method to just that particular instance. After calling m.method_gen("foo"), m.foo() calls the method defined by method_gen. That call does not affect other instances of MethodGen like n.

  • 陪你到老 回复

    有趣的是,以前很难做到这一点,似乎很难维护(可能会使一些其他开发人员想吊死你)。

    我更改了一些代码,以便您可以了解更多情况。

    class MethodGen(object):
    
        def method_gen(self, name):
            print("Creating a method here")
            def method(*args, **kwargs):
                print("Calling method")
                print(args) # so we can see what is actually being outputted
    
            # Below are the two lines of code I need help understanding with
            method.__name__ = name # These the method name equal to name (i.e. we can call the method this way) 
            # The following is adding the new method to the current class.
            setattr(self, name, method.__get__(self)) # Adds the method to this class
    
            # I would do: setattr(self, name, method) though and remove the __get__
    
            return method # Returns the emthod
    
    m = MethodGen()
    test = m.method_gen("my_method") # I created a method in MethodGen class called my_method
    test("test") # It returned a pointer to the method that I can use
    m.my_method("test") # Or I can now call that method in the class.
    m.method_gen("method_2")
    m.method_2("test2")