zoukankan      html  css  js  c++  java
  • 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 以及只读属性。它还允许您根据值或类型请求进行属性验证。您可以在许多不同的领域应用描述符,但是使用时需保持谨慎的态度,避免由于覆盖普通对象行为而产生不必要的代码复杂性。

  • 相关阅读:
    JDK版本1.6和6.0到底指什么
    分布式存储Memcache替代Session方案
    Spring事务隔离级别和传播特性
    高性能并发系统架构应该如何设计?关键是什么?12306
    Idea无法DEBUG的问题
    springboot(三 使用mybatis +springboot 完成简单的增删改查)
    springboot(二 如何访问静态资源和使用模板引擎,以及 全局异常捕获)
    spring boot (入门简介 demo)
    java1.8新特性(optional 使用)
    java1.8 新特性(关于 match,find reduce )操作
  • 原文地址:https://www.cnblogs.com/youxin/p/3061341.html
Copyright © 2011-2022 走看看