python method unbound bound static区别和descriptor描述符解释

来自:http://stackoverflow.com/questions/114214/class-method-differences-in-python-bound-unbound-and-static

python 中function和method是不同的。

What is the difference between the following class methods?

Is it that one is static and the other is not?

classTest(object):

  def method_one(self):
 
     print"Called method_one"
 
  def method_two():

     print"Called method_two"

a_test =Test()
a_test.method_one()
a_test.method_two()

In Python, there is a distinction between bound and unbound methods.

Basically, a call to a member function (like method_one), a bound function

a_test.method_one()

is translated to

Test.method_one(a_test)

i.e. a call to an unbound自由的,未绑定的 method. Because of that, a call to your version of method_two will fail with aTypeError

>>> a_test =Test()

>>> a_test.method_two()

Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError:
method_two
() takes no arguments (1 given)

You can change the behavior of a method using a descriptor

classTest(object):

def method_one(self):

print"Called method_one"

@staticmethod

def method_two():

print"Called method two"

The decorator tells the built-in default metaclass type (the class of a class, cf. this question) to not create bound methods for method_two.

Now, you can invoke static method both on an instance or on the class directly:

>>> a_test =Test()

>>> a_test.method_one()Called method_one >>> a_test.method_two() Called method_two >>>Test.method_two() Called method_two
另一回答:

Methods in Python are a very, very simple thing once you understood the basics of the descriptor system. Imagine the following class:

class C(object):

def foo(self):pass

Now let's have a look at that class in the shell:

>>> C.foo
<unbound method C.foo》

>>>> C.__dict__['foo'] (ps:A.__dict__ is a dictionary storing A attributes,)

<function foo at 0x17d05b0>

As you can see if you access the foo attribute on the class you get back an unbound method, however inside the class storage (the dict) there is a function. Why's that? The reason for this is that the class of your class implements a __getattribute__ that resolves descriptors. Sounds complex, but is not.C.foo is roughly equivalent to this code in that special case:

>>> C.__dict__['foo'].__get__(None, C)

<unbound method C.foo>

That's because functions have a __get__ method which makes them descriptors. If you have an instance of a class it's nearly the same, just that None is the class instance:

>>> c = C()

>>> C.__dict__['foo'].__get__(c, C)

<bound method C.foo of <__main__.C object at 0x17bd4d0>>

Now why does Python do that? Because the method object binds the first parameter of a function to the instance of the class. That's where self comes from. Now sometimes you don't want your class to make a function a method, that's where staticmethod comes into play:

class C(object):@staticmethoddef foo():pass

The staticmethod decorator wraps your class and implements a dummy __get__ that returns the wrapped function as function and not as a method:

>>> C.__dict__['foo'].__get__(None, C)<function foo at 0x17d0c30>

8.4. unbound method和bound method

>>> class C(object):
...     def foo(self):
...         pass
...
>>> C.foo
<unbound method C.foo>
>>> C().foo
<bound method C.foo of <__main__.C object at 0xb76ddcac>>
>>>

为什么 C.foo 是一个 unbound method , C().foo 是一个 bound method ? Python 为什么这样设计?

答:这是问题 http://stackoverflow.com/questions/114214/class-method-differences-in-python-bound-unbound-and-static

来自Armin Ronacher(Flask 作者)的回答:

如果你明白python中描述器(descriptor)是怎么实现的, 方法(method) 是很容易理解的。

上例代码中可以看到,如果你用类 C 去访问 foo 方法,会得到 unbound 方法,然而在class的内部存储中它是个 function, 为什么? 原因就是 C 的类 (注意是类的类) 实现了一个 __getattribute__ 来解析描述器。听起来复杂,但并非如此。上例子中的 C.foo 等价于:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

这是因为方法 foo 有个 __get__ 方法,也就是说, 方法是个描述器。如果你用实例来访问的话也是一模一样的:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0xb76ddd8c>>

只是那个 None 换成了这个实例。

现在我们来讨论,为什么Python要这么设计?

其实,所谓 bound method ,就是方法对象的第一个函数参数绑定为了这个类的实例(所谓 bind )。这也是那个 self 的由来

当你不想让类把一个函数作为一个方法,可以使用装饰器 staticmethod

>>> class C(object):
...     @staticmethod
...     def foo():
...         pass
...
>>> C.foo
<function foo at 0xb76d056c>
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0xb76d056c>

staticmethod 装饰器会让 foo 的 __get__ 返回一个函数,而不是一个方法。

8.5. 那么,function,bound method 和 unbound method 的区别是什么?

一个函数(function)是由 def 语句或者 lambda 创建的。

当一个函数(function)定义在了class语句的块中(或者由 type 来创建的), 它会转成一个 unbound method , 当我们通过一个类的实例来 访问这个函数的时候,它就转成了bound method , bound method 会自动把这个实例作为函数的地一个参数。

所以, bound method 就是绑定了一个实例的方法, 否则叫做 unbound method .它们都是方法(method), 是出现在 class 中的函数。

一篇文章:

 

Python之美[从菜鸟到高手]--装饰器之使用情景分析

   你知道property的实现原理吗? 你知道@classmethod,@staticmethod的原理吗?如果你摇头了,那么这篇文章你肯定不能错过,让我们开始吧?

        在说property之前,我们需要理解描述符,因为不管property还是classmethod都是构建在描述符的基础上,那么到底什么是描述符呢?

        描述符,用一句话来说,就是将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性)。而这种特殊类型的类就是实现了__get__,__set__,__delete__的新式类(即继承object)。

class Descriptor(object):
    def __get__(self,object,type):
        print 'get',self,object,type

    def __set__(self,object,value):
        print 'set',self,object,value

class Demo(object):
    desc= Descriptor()

demo=Demo()
demo.desc   # get <__main__.Descriptor object at 0x0269BC90> <__main__.Demo object at 0x0269BD50> <class '__main__.Demo'>
demo.desc='my descriptor' #set <__main__.Descriptor object at 0x0269BC90> <__main__.Demo object at 0x0269BD50> my descriptor

  其中Descriptor就是一个描述符类,只要实现了__get__等三种方法中一个或几个都是描述符类。

从输出结果我们可以看出,__get__方法中的object就是调用描述符对象的实例,即对象demo,type就是demo的类。你也许觉得好奇,为啥__get__的参数是这个,不急,看完下面你就懂了。

      刚才说了,描述符类的实例必须是类属性,我们将描述符类指定为对象属性,代码如下:

class DescTest(object):  
    def __init__(self):  
        self.desc=Descriptor()  
  
test=DescTest()  
test.desc  

你会发现,压根没有调用__get__方法,那么调用过程到底是怎么样的呢?

其实调用test.desc,等价于调用type(test).__dict__['desc'].__get__(test,type(test)),懂了吧!因为DescTest类没有'desc'属性。

调用Demo.desc,等价于调用Demo.__dict__['desc'].__get__(None,Descriptor),所以类也可以直接调用。

     那么描述符到底有啥作用呢?,看下面简单property用法。

class B(object):
    def __init__(self):
        self.__name='lwy'

    def getname(self):
        return self.__name

    name=property(getname)

b=B()
print b.name  # lwy

 那么property的原理是什么呢? 其实property是一个描述符类,当你调用b.name时,其实调用的就是property.__get__方法

我们可以自己实现自己的property描述符类,我们只实现gettters方法,代码如下:

class myProperty(object):
    def __init__(self,get):
        self.get=get
    def __get__(self,object,type):
        '''
            object就是调用该方法的对象实例,type是实例类型
         调用getters方法有多种,如:
         1.最简单方法: 调用传递过来的getters函数,将对象作为参数(即类中的self)
         2.获取函数名字,使用getattr调用对象的成员函数
                funcname=self.get.func_name
                return getattr(object,funcname)()
         为了简单,我使用第一种方法
        '''
        return self.get(object)
class B(object):
    def __init__(self):
        self.__name='lwy'

    def getname(self):
        return self.__name

    @myProperty
    def name1(self):
        return self.__name

    name=myProperty(getname)

b=B()
print b.name   #'lwy'
print b.name1  #'lwy'

 可以看出,调用name=myProperty(getname)时,会将name.get方法设置为getname方法,当调用b.name时,将调用name的__get__方法。

    同时我们也可以看出,@myProperty装饰器使用,因为本质上,@myProperty def name1 ->>>等价于,name1=myProperty(name1)。

    到这里,大家对装饰器类应该熟悉了解了,那么实现@staticmethod,@classmethod也就很自然了。

看下面代码:

class TestDemo(object):
    name='lwy'

    def staticShow():
        print 'static'
    staticShow=staticmethod(staticShow)

    def classShow(cls):
        print cls.name
    classShow=classmethod(classShow)

t=TestDemo()
t.staticShow() #static
t.classShow()  #lwy

这就是Python支持完整面向对象的方法,通过使用特殊方法实现。下面我们实现自己的staticmethod和classmethod。

代码如下:

class myStaticmethod(object):
    def __init__(self,method):
        self.staticmethod=method
    def __get__(self,object,type):
        return self.staticmethod

class myClassmethod(object):
    def __init__(self,method):
        self.classmethod=method
    def __get__(self,cobject,type):
        def _deco():
            return self.classmethod(type)  #因为调用类方法,第一个参数是类(cls)
        return _deco

class myTestDemo(object):
    name='lwy'

    def staticShow():
        print 'static'
    staticShow=myStaticmethod(staticShow)

    def classShow(cls):
        print cls.name
    classShow=myClassmethod(classShow)

t=myTestDemo()
t.staticShow() #static
t.classShow()  #lwy

面myClassmethod的__get__使用了装饰器编程方式,不熟悉的童鞋可参考如下文章:http://blog.csdn.net/yueguanghaidao/article/details/10089181

参考:http://blog.csdn.net/yueguanghaidao/article/details/10291147

ibm:http://www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/

Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用。Python 描述符是一种创建托管属性的方法。除了其他优点外,托管属性还用于保护属性不受修改,或自动更新某个依赖属性的值。

描述符增加了对 Python 的理解,改善了编码技能。本文介绍了描述符协议,并演示了如何创建和使用描述符。

描述符协议

Python 描述符协议 只是一种在模型中引用属性时指定将要发生事件的方法。它允许编程人员轻松、有效地管理属性访问:

  • set
  • get
  • delete

在其他编程语言中,描述符被称作 setter 和 getter,而公共函数用于获得 (Get) 和设置 (Set) 一个私有变量。Python 没有私有变量的概念,而描述符协议可以作为一种 Python 的方式来实现与私有变量类似的功能。

总的来说,描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。这些方法为 __get____set__ 和__delete__。如果这些方法中的任何一个针对某个对象定义,那么它就被认为是一个描述符。通过 清单 1 进一步了解这些方法。

清单 1. 描述符方法
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)

其中:

__get__ 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。

__set__ 将在属性分配操作中调用。不会返回任何内容。

__delete__ 控制删除操作。不会返回内容。

需要注意,描述符被分配给一个类,而不是实例。修改此类,会覆盖或删除描述符本身,而不是触发它的代码。

需要使用描述符的情况

考虑 email 属性。在向该属性分配值之前,需要对邮件格式进行检验。该描述符允许通过一个正则表达式处理电子邮件,然后对格式进行检验后将它分配给一个属性。

在其他许多情况下,Python 协议描述符控制对属性的访问,如保护 name 属性

创建描述符

您可以通过许多方式创建描述符:

  • 创建一个类并覆盖任意一个描述符方法:__set____ get__ 和 __delete__。当需要某个描述符跨多个不同的类和属性,例如类型验证,则使用该方法。
  • 使用属性类型,这种方法可以更加简单、灵活地创建描述符。
  • 使用属性描述符,它结合了属性类型方法和 Python 描述符。

以下示例在其操作方面均相似。不同之处在于实现方法。

使用类方法创建描述符

清单 2 演示在 Python 中控制属性分配非常简单。

清单 2. 使用类方法创建描述符
class Descriptor(object):

    def __init__(self):
        self._name = ''

    def __get__(self, instance, owner):
        print "Getting: %s" % self._name
        return self._name

    def __set__(self, instance, name):
        print "Setting: %s" % name
        self._name = name.title()

    def __delete__(self, instance):
        print "Deleting: %s" %self._name
        del self._name

class Person(object):
    name = Descriptor()

使用这些代码并查看输出:

>>> user = Person()
>>> user.name = 'john smith'
Setting: john smith
>>> user.name
Getting: John Smith
'John Smith'
>>> del user.name
Deleting: John Smith

过以下方法覆盖父类的 __set__()__get__() 和 __delete__() 方法,创建一个描述符类:

  • get 将输出 Getting
  • delete 将输出 Deleting
  • set 将输出 Setting

并在分配之前将属性值修改为标题(第一个字母大写,其他字母为小写)。这样做有助于存储和输出名称。

大写转换同样可以移动到 __get__() 方法。_value 有一个初始值,并根据 get 请求转换为标题。

 

使用属性类型创建描述符

虽然 清单 2 中定义的描述符是有效的且可以正常使用,但是还可以使用属性类型的方法。通过使用 property(),可以轻松地为任意属性创建可用的描述符。创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None),其中:

  • fget:属性获取方法
  • fset:属性设置方法
  • fdel:属性删除方法
  • doc:docstring

使用属性重写该例子,如 清单 3 所示。

清单 3. 使用属性类型创建描述符
class Person(object):
    def __init__(self):
        self._name = ''

    def fget(self):
        print "Getting: %s" % self._name
        return self._name
    
    def fset(self, value):
        print "Setting: %s" % value
        self._name = value.title()

    def fdel(self):
        print "Deleting: %s" %self._name
        del self._name
    name = property(fget, fset, fdel, "I'm the property.")

使用该代码并查看输出:

>>> user = Person()
>>> user.name = 'john smith'
Setting: john smith
>>> user.name
Getting: John Smith
'John Smith'
>>> del user.name
Deleting: John Smith

显然,结果是相同的。注意fgetfset 和 fdel 方法是可选的,但是如果没有指定这些方法,那么将在尝试各个操作时出现一个AttributeError 异常。例如,声明 name 属性时,fset 被设置为 None,然后开发人员尝试向 name 属性分配值。这时将出现一个AttributeError 异常。

这种方法可以用于定义系统中的只读属性。

name = property(fget, None, fdel, "I'm the property")
user.name = 'john smith'

输出为:

Traceback (most recent call last):
File stdin, line 21, in mоdule
user.name = 'john smith'
AttributeError: can't set attribute
 

使用属性修饰符创建描述符

可以使用 Python 修饰符创建描述符,如 清单 4 所示。Python 修饰符是对 Python 语法的特定修改,能够更方便地更改函数和方法。在本例中,将修改属性管理方法。在 developerWorks 文章 Decorators make magic easy 中寻找更多有关应用 Python 修饰符的信息。

清单 4. 使用属性修饰符创建描述符
class Person(object):

    def __init__(self):
        self._name = ''

    @property
    def name(self):
        print "Getting: %s" % self._name
        return self._name

    @name.setter
    def name(self, value):
        print "Setting: %s" % value
        self._name = value.title()

    @name.deleter
    def name(self):
        print ">Deleting: %s" % self._name
        del self._name
 

在运行时创建描述符

前面的所有例子都使用了 name 属性。该方法的局限性在于需要对各个属性分别覆盖 __set__()__get__() 和 __delete__()清单 5 提供了一个可能的解决方案,帮助开发人员在运行时添加 property 属性。该解决方案使用属性类型构建数据描述符。

清单 5. 在运行时创建描述符
class Person(object):

    def addProperty(self, attribute):
        # create local setter and getter with a particular attribute name 
        getter = lambda self: self._getProperty(attribute)
        setter = lambda self, value: self._setProperty(attribute, value)

        # construct property attribute and add it to the class
        setattr(self.__class__, attribute, property(fget=getter, \
                                                    fset=setter, \
                                                    doc="Auto-generated method"))

    def _setProperty(self, attribute, value):
        print "Setting: %s = %s" %(attribute, value)
        setattr(self, '_' + attribute, value.title())    

    def _getProperty(self, attribute):
        print "Getting: %s" %attribute
        return getattr(self, '_' + attribute)

让我们运行这段代码:

>>> user = Person()
>>> user.addProperty('name')
>>> user.addProperty('phone')
>>> user.name = 'john smith'
Setting: name = john smith
>>> user.phone = '12345'
Setting: phone = 12345
>>> user.name
Getting: name
'John Smith'
>>> user.__dict__
{'_phone': '12345', '_name': 'John Smith'}

这将在运行时创建 name 和 phone 属性。它们可以根据相应的名称进行访问,但是按照 _setProperty 方法中的定义,将在对象名称空间目录中存储为 _name 和 _phone。基本上,name 和 phone 是对内部的 _name 和 _phone 属性的访问符。

当开发人员尝试添加 name property 属性时,您可能对系统中的 _name 属性存在疑问。实际上,它将用新的 property 属性覆盖现有的 _name 属性。这些代码允许控制如何在类内部处理属性。

 

结束语

Python 描述符可以利用新的样式类实现强大而灵活的属性管理。通过结合使用描述符,可以实现优雅的编程,允许创建 Setters 和 Getters 以及只读属性。它还允许您根据值或类型请求进行属性验证。您可以在许多不同的领域应用描述符,但是使用时需保持谨慎的态度,避免由于覆盖普通对象行为而产生不必要的代码复杂性。

原文地址:https://www.cnblogs.com/youxin/p/3061341.html