|
很高深的问题. __get__( ) 是一个特殊的函数
详细的回答在这里:
Take a look at this:
class Member:
def __get__(self, instance, owner):
print("I'm a member of {0}".format(instance))
return self
class C:
x = Member()
c = C()
c.x
prints out
I'm a member of <__main__.C object at ...>
See ? The Member instance being a member of some other class is notified whenever it is accessed. Where can it be useful you may ask ? Oh, it is the key to the magic "self" in Python.
Consider the following most simple piece of code:
class C:
def foo(self):
print(self)
Have you ever thought what "self" is ? I mean - it obviously is an argument containing a reference to the instance being called, but where did it come from ? It doesn't even have to be called "self", it is just a convention, the following will work just as well:
class C:
def foo(magic):
print(magic)
And so it turns out that somehow at the moment of the invocation the first argument of every method points to the containing instance. How is it done ?
What happens when you do
c = C()
c.foo()
anyhow ? At first sight, access to c.foo should return a reference to a method - something related to C and irrelevant to c. But it appears that the following two accesses to foo
c1 = C()
c1.foo
c2 = C()
c2.foo
fetch different things - c1.foo returns a method with its first argument set to c1 and c2.foo - to c2. How could that happen ? The key here is that you access a method (which is a member of a class) through a class instance. The class itself contains its methods in a half-cooked "unbound" state, they don't have any "self":
class C:
def foo(self):
pass
print(C.foo)
print(C().foo)
prints out
<function foo at ...>
<bound method C.foo of <__main__.C object at ...>>
See ? When fetched directly from a class, a method is nothing but a regular function, it is not "bound" to anything. You can even call it, but you will have to provide its first argument "self" by yourself as you see fit:
class C:
def foo(self):
print(self)
C.foo("123")
prints out
123
But as soon as you instantiate and fetch the same method through an instance, the magic __get__ method comes into play and allows the returned reference to be "bound" to the actual instance. Something like this:
class Method:
def __init__(self, target):
self._target = target
def __get__(self, instance, owner):
self._self = instance # <<<< binding ahoy !
return self
def __call__(self, *args, **kwargs):
return self._target(self._self, *args, **kwargs)
class C:
foo = Method(lambda self, *args, **kwargs:
print(self, args, kwargs))
c = C()
print(c)
c.foo(1, 2, foo = "bar")
prints out
<__main__.C object at 0x00ADA0D0>
<__main__.C object at 0x00ADA0D0> (1, 2) {'foo': 'bar'}
And so I could demonstrate a reimplementation of a major language feature in a few lines. May be not apparently useful most of the time, such experience certainly makes you understand the language better.
One more thing, have I told you Python was cool ? :) |
|