python面向对象-属性和方法

一 属性:
    在理清object和type关系中谈及到在python中一切都是对象,包括所谓的类,而类的工作方式可以看作是一个工厂方法,type类生产类型对象,类对象生产普通的对象。而type类本身是自身的对象,这可以从对象的__class__属性中追溯到。
    看看对象中的属性到底是怎么一回事?首先可以这样去定义属性:

    
>>> class C(object):
    >>>    def __init__ (self):
    >>>        self.objattr = "attr on object"
    >>>    classattr = "attr on class"
    >>> cobj = C()
    >>> cobj.inattr2 = "attr2 on instance"


    这里定义了一个C的类型对象,和一个cobj普通对象,用三种方式定义了三个属性classattr、objattr、inattr2,这三个属性到底有没有区别呢?

    >>> C.__dict__.keys()

    可以看到有一个属性:classattr,而看不到其他的两个属性,而这两个属性被隐藏到了cobj.__dict__中。

    python中我们把类中自定义的属性(与python提供的属性对立,比如__bases__属性)分为两类,一类称作类属性,另一类称作实例属性,类属性就是在定义类的时候,添加在类中的属性,它们存放在类型对象(这里是C)的__dict__属性中,而实例属性是存放在实例的__dict__属性中的。

>>> cobj.classattr
    'attr on class'
>>> cobj.inattr2
    'attr2 on instance'


    这两类属性都能通过实例对象来访问,而类不能直接访问实例属性。

二 对象的方法

class C(object):
    def f(self):
        return "f defined in class"

当在类中定义一个函数的时候(f),类似于属性,这个函数对象也会被存放在__dict__对象中,当用实例对象去访问这个函数对象的时候,这个时候的函数其实是一个<bound method>类型的对象,这种类型的对象就是一个可封装了的可调用的对象(调用在类中定义的函数,就是上面的f函数)。
可以通过这样的方式来创建一个<bound method>对象:

>>> c = C()
>>> a = C.__dict__['f'].__get__(c, C)


在讨论函数到<bound method>的转换之前,先解释一个概念:Descriptor(描述符)

>>> def test_func():
>>>     pass
>>> dir(test_func)

可以看到列表中包含了一个__get__(self, obj, cls=None)方法,这个方法的意义就产生一种协议叫做描述符。当一个属性对象被访问的时候(例如:obj.attrname),首先这个属性要包含在被引用对象(obj对象)的__dict__对象中,如果这个属性引用的对象(attrname对象)包含了__get__方法,那么返回的就是调用__get__方法返回的值。
还有其他两个属性分别是__set__(self, obj, var), __delete__(self, obj)分别对应两种操作"赋值"(objectname.attr = xx)和"del" (del objectname.attrname)。
其实描述符的作用很简单,通俗点讲当为一个对象的属性执行查找、赋值、删除操作的时候,额外的再做些事情,这些属性就显得比较特殊了。

举个例子:

class Desc(object):
    def __init__(self, testv="a"):
        self.testv = testv
    def __get__(self, obj, cls = None):
        print obj, cls
        return self.testv + "get"
    def __set__(self, obj, val):
        self.testv = val + "set"
    def __delete__(self, obj):
        #del self.testv
        print 'delete'
class T(object):
    des = Desc("aaaaa")

>>> t = T()
>>> t.des #调用__get__方法
<__main__.T object at 0x92095cc> <class '__main__.T'>
'aaaaaget'
>>> t.des = "bbb" #调用__set__方法
>>> t.des
'bbbsetget'
>>> del t.des #调用__delete__方法
delete
>>> t.des
'bbbsetget'


>>> t.__dict__['des'] = "another value" 
>>> t.des #奇怪吧?为什么没有变呢,这个是跟属性的访问顺序有关的,看下去
'bbbsetget'
>>> T.des = "new str" #替换了Desc对象
>>> y.des
'anthoer value'


* 注意:类只有访问属性的时候(class.name.attrname),才会调用__get__方法,赋值和删除将会替换和删除Descriptor属性对象


Descriptor的两种类型
1. Data Descriptor(数据描述符): 包含__get__和__set__方法的对象。上面的实例就是属于这种描述符。

2. Non-Data Descriptor(非数据描述符): 只包含__get__方法的对象。

class GetonlyDesc(object):
    def __get__(self, obj, typ=None):
        pass
class C(object):
    d = GetonlyDesc()    
>>> cobj = C()
>>> cobj.d #调用__get__ 方法
>>> cobj.d = "setting a value"
>>> cobj.d #从__dict__ 获取
'setting a value'

非数据描述符的作用是:当一个属性没有值的时候,它会调用__get__返回值,如果属性被设置了值后将会在__dict__读取和修改值。

bottle中的一段代码:

class lazy_attribute(object):
    ''' A property that caches itself to the class object. '''
    def __init__(self, func):
        functools.update_wrapper(self, func, updated=[])
        self.getter = func
    def __get__(self, obj, cls):
        value = self.getter(cls)
        setattr(cls, self.__name__, value)
        return value


class SimpleTemplate(BaseTemplate):
    @lazy_attribute
    def re_pytokens(cls):
        return re.compile(r"", re.VERBOSE)

第一次通过SimpleTemplate对象(假设对象的名称叫stemplate)访问re_pytokens属性的时候,调用__get__方法,该方法中调用定义的re_pytokens函数,并为stemplate添加了re_pytokens属性,并赋值,其实就是向stemplate.__dict__对象中添加对象。当再去访问这个属性的时候,以前的__get__方法就不会再被调用了,而是直接访问__dict__了。
再回过头来看a = C.__dict__['d'].__get__(c, C)是不是觉得豁然开朗,当访问c.d的时候,就是调用的函数的__get__方法,而对象<bound method>对象就是这么产生的。


总结一下属性访问的顺序(objectname.attrname):
1,当attrname对象是python内建的话直接返回值。
2,到objectname.__class__.__dict__中检查attrname存在并且是否是符号描述符,如果存在且是的话调用__get__返回结果。
3,到objectname.__dict__对象中检查,有则返回值。
4,到objectname.__class__.__dict__中检查这attrname是否存在并且是否是非描述浮,如果是调用__get__返回结果,如果不是描述符,则直接返回值。

5,否则报错

英文原文:http://www.cafepy.com/article/python_attributes_and_methods/contents.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值