Python笔记(4)类(上)属性与描述符

部分参考自:http://www.geekfan.net/7862/

新式类与经典类

2和3不一样,3都是新式类。

新式类和经典类的区别:

class A:
    #classic class
    """this is class A"""
    pass
    __slots__=(‘x‘,‘y‘)
    def test(self):
        # classic class test
        """this is A.test()"""
        print "A class"
class B(object):
    #new class
    """this is class B"""
    __slots__=(‘x‘,‘y‘)
    pass
    def test(self):
        # new class test
        """this is B.test()"""
        print "B class"

if __name__ == ‘__main__‘:
    a=A()
    b=B()
    print dir(a)
    print dir(b)
[‘__doc__‘, ‘__module__‘, ‘__slots__‘, ‘test‘]
[‘__class__‘, ‘__delattr__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__hash__‘, ‘__init__‘, ‘__module__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__slots__‘, ‘__str__‘, ‘__subclasshook__‘, ‘test‘, ‘x‘, ‘y‘]

  新式类要指明父类,上面代码class B 声明他的父类为object。

python是动态语言,可以动态的添加属性。

>>> a.x = 1
>>> a
<__main__.A instance at 0x05BBB620>
>>> a.x
1

 __slots__槽,属性限制了实例b只能添加x,y属性,a是经典类,可以继续添加,但是b是新式类不能继续添加。

>>> a.z = 2
>>> a.z
2
>>> b.z = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ‘B‘ object has no attribute ‘z‘

  

>>> help(a)
Help on instance of A in module __main__:

class A
 |  this is class A
 |
 |  Methods defined here:
 |
 |  test(self)
 |      this is A.test()

>>> help(b)
Help on B in module __main__ object:

class B(__builtin__.object)
 |  this is class B
 |
 |  Methods defined here:
 |
 |  test(self)
 |      this is B.test()
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  x
 |
 |  y

  B类由于是新式类 __slots__起作用了,尽量使用新式类,因为这样python2,3中都能跑。

属性和封装

实例类型

__init__  双下划线是特殊方法,__init__定义实例属性,owner,country是实例属性,country是类属性。

调用的时候,比如类属性和实例属性名字一样,调用实例属性。如果没有实例属性,则去寻找是不是存在类属性。

class Car(object):
    country = u‘中国‘
    def __init__(self,owner=None):
        self.owner = owner
        self.country = "china"

if __name__ == ‘__main__‘:
    a = Car(u‘小张‘)
    print a.country

    a.country = u‘美国‘
    print a.country
    print "--------------------------"
    del a.country
    print a.country
>>> china
美国
--------------------------
中国
私有属性

私有属性只在函数内部可见。通过get,set方法对其赋值更改。

在变量前加两个下划线__ 可以间接访问,只加一个下划线_模块私有化。变量前后各两个下划线__是系统自带的属性。

class Car(object):
    def __init__(self,owner=None):
        self.__owner = owner

    def getOwner(self):
        return self.__owner
    def setOwner(self, value):
        self.__owner = value

if __name__ == ‘__main__‘:
    a = Car(u‘黑板客‘)
    print a.getOwner()

  

>>> a.owner
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ‘Car‘ object has no attribute ‘owner‘
>>> a.getOwner()
u‘\u9ed1\u677f\u5ba2‘
>>> dir(a)
[‘_Car__owner‘, ‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__hash__‘, ‘__init__‘, ‘__module__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘getOwner‘, ‘setOwner‘]
>>> a._Car__owner
u‘\u9ed1\u677f\u5ba2‘

  

描述符

装饰器描述符

@property @xx.setter @xx.deleter

可以把get,set方法变成属性访问。

class Car(object):
    def __init__(self,owner=None):
        self._owner = owner

    @property
    def owner(self):
        return self._owner
    @owner.setter
    def owner(self, value):
        self._owner = value
    @owner.deleter
    def owner(self):
        self._owner = None

if __name__ == ‘__main__‘:
    a = Car(u‘你大爷‘)
    print a.owner
    del a.owner
    print a.owner 
你大爷
None

 这样一个owner get,set,del要定义三个,如果有别的属性,则又需要三个,这样会产生冗余,重复代码。

__getattr__, __setattr__, __delattr__

__getattr__  在变量的__dict__和_class__.__dict__中没有找到属性,就会调用__getattr__,如果有的话,就直接调用__dict__中的值了。

__setattr__ 变量赋值

__delattr__ 删除变量

class Car(object):
    country = u‘中国‘
    #__slots__=(‘length‘,‘width‘,‘height‘,‘owner‘,‘__dict__‘)

    def __init__(self, length, width, height, owner=None):
        self.owner = owner
        self.length = length
        self.width = width
        self.height = height

    def __getattr__(self,name):
        print "__getattr__",name
        return self.__dict__.get(name,None)

    def __setattr__(self,name,value):
        print "__setattr__",name
        if name!=‘owner‘:
            assert value>0, name+" must larger than 0"
        self.__dict__[name]=value

    def __delattr__(self,name):
        print "__delattr__",name
        if name==‘owner‘:
            self.__dict__[name]=None

if __name__ == ‘__main__‘:
    a = Car(1.2,1.4,1.5,u‘二大爷‘)

输出:  

__setattr__ owner
__setattr__ length
__setattr__ width
__setattr__ height

 把__slots__加上之后,因为可以访问__setattr__所以还是可以任意的加属性而不会报错,要使得slots有效果,得在__setattar__里面修改代码:

    def __getattr__(self,name):
        print "__getattr__",name
        assert name in self.__slots__, "Not have this attribute "+name
        return self.__dict__.get(name,None)

    def __setattr__(self,name,value):
        print "__setattr__",name
        assert name in self.__slots__, "Not have this attribute "+name
        if name!=‘owner‘:
            assert value>0, name+" must larger than 0"
        self.__dict__[name]=value

    def __delattr__(self,name):
        print "__delattr__",name
        assert name in self.__slots__, "Not have this attribute "+name
        if name==‘owner‘:
类描述符

  描述符可以用作类的属性,数据描述符__get__,__set__,__del__。

class PositiveNum(object):
    def __init__(self):
        self.default = 1
        self.data = {}

    def __get__(self, instance, owner):
        # instance = x
        # owner = type(x)
        print "__get__",instance,owner
        return self.data.get(instance, self.default)

    def __set__(self, instance, value):
        # instance = x
        print "__set__",instance,value
        try:
            assert int(value)>0
            self.data[instance] = value
        except AssertionError:
            print "ERROR: "+str(value)+" is not positive number."
        except:
            print "ERROR: "+str(value)+" is not number value."

    def __delete__(self,instance):
        print "__delete__",instance
        del self.data[instance]

class Car(object):
    country = u‘中国‘
    length = PositiveNum()
    width = PositiveNum()
    height = PositiveNum()
    __slots__=(‘owner‘,‘length‘,‘width‘,‘height‘)

    def __init__(self, length, width, height, owner=None):
        self.owner = owner
        self.length = length
        self.width = width
        self.height = height

if __name__ == ‘__main__‘:
    a = Car(1.2,1.4,1.5,u‘黑板客‘)
    b = Car(2.2,2.4,2.5,u‘小明‘)
    print a.length
    a.length=1

  当解释器遇到print a.length时,它会把length当作一个带有__get__方法的描述符,调用a.length.__get__方法并将方法的返回值打印,这和上面的property相似。__get__接收两个参数:instance 实例对象,这里就是a.length中的a,另一个是实例的类型Car。在一些文档中,Car被称作描述符的所有者(owner)。如果需要访问Car.length,python将会调用Car.length.__get__(None,Car)。可以看到第一个参数要么是实例,要么是None。

  当解释器看到a.length = 1时,Python识别出length是一个带__set__方法的描述符,于是就调用a.length.__set__(a,100),第一个参数instance是实例,第二个是赋值。

  删除时Car.length.__delete__(a)。

  每个PositiveNum维护着一个字典,其中保存着所有者实例和对应数据的映射关系。调用a.length时,__get__方法会找出与a相关的数据,并发挥结果,如果不存在就返回一个默认值。__set__采用的方式相同,但是会包含额外的非法检查。

  描述符作用与类的层次上,每一个类的实例都共享同一个描述符。所以不同的实例对象不得不手动的管理不同的状态,需要显示的将参数精确的传递给__get__,__set__以及__delete__方法。

  如果将PositiveNum中的 data = {}去掉,由于描述符是基于类层面的,他们会共享同一个类属性,这就是使用字典的原因。__get__,__set__参数也指明哪一个实例,以实例为字典的key。

错误示例:

class PositiveNum(object):
    def __init__(self,value):
        self.val = value

    def __get__(self, instance, owner):
        # instance = a,b
        # owner = Car
        print "__get__",instance,owner
        return self.val

    def __set__(self, instance, value):
        # instance = a,b
        print "__set__",instance,value
        try:
            assert int(value)>0
            self.val = value
        except AssertionError:
            print "ERROR: "+str(value)+" is not positive number."
        except:
            print "ERROR: "+str(value)+" is not number value."  

    def __delete__(self,instance):
        print "__delete__",instance
        self.val = None

    #def __getattribute__(self,name):
        #print self, name

class Car(object):
    country = u‘中国‘
    length = PositiveNum(0)
    width = PositiveNum(0)
    height = PositiveNum(0)
    #__slots__=(‘owner‘,‘length‘,‘width‘,‘height‘)

    def __init__(self, length, width, height, owner=None):
        self.owner = owner
        self.length = length
        self.width = width
        self.height = height

if __name__ == ‘__main__‘:
    a = Car(1.2,1.4,1.5,u‘黑板客‘)
    b = Car(2.2,2.4,2.5,u‘小明‘)

  

a.length
__get__ <__main__.Car object at 0x098E61B0> <class ‘__main__.Car‘>
Out[39]: 2.2

b.length
__get__ <__main__.Car object at 0x098E6230> <class ‘__main__.Car‘>
Out[40]: 2.2

 虽然a定义的1.2,但由于与b公用一个类属性,所以也变成了2.2。

时间: 2024-12-16 22:25:05

Python笔记(4)类(上)属性与描述符的相关文章

[python 笔记4]类(面向对象?)

1.定义 __metaclass__=type class Person: def __init__(self,name,age): self.name=name self.age=age def setName(self,name): self.name=name def getName(self): return self.name def setAge(self,age): self.age=age def getAge(self): return self.age p1=Person('

小甲鱼Python笔记(类)

类和对象 类的构造方法 def __init__(): 1 class People: 2 def __init__(self,name): 3 self.name = name 注意:在构造方法中的变量不用再次声明,必须有self,创建类的对象是用= 类的私有成员 让方法或者类变为私有,只要在它的名字前加上双下划线 1 class People: 2 __name = 'laowang' 3 def getname(self): 4 return self.__name 注意:类的私有成员可以

python中的类中属性元素加self.和不加self.的区别

在类中,self只能在函数中使用,表示的是实例属性,就是每个实例可以设置不值,而不相互影响. 如果在类级别使用没有self的属性,是类属性,一般作为全局变量来用的.事实上:就是一个是类属性 一个是对象属性 类和实例都可以访问到属性 当你想让这个变量成为这个类的子变量时 class lc(): def __init__(self): self.a = 5 smalllc = lc() print smalllc. 如果不加self的话 , 那么就不能 smalllc.a 这样访问了

python2.7高级编程 笔记二(Python中的描述符)

Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些"中级"的语言特性有着完善的文档,并且易于学习. 但是这里有个例外,那就是描述符.至少对于我来说,描述符是Python语言核心中困扰我时间最长的一个特性.这里有几点原因如下: 有关描述符的官方文档相当难懂,而且没有包含优秀的示例告诉你为什么需要编写描述符(我得为Raymond Hettinger辩护一

Python属性描述符(一)

描述符是对多个属性运用相同存取逻辑的一种方式,,是实现了特性协议的类,这个协议包括了__get__.__set__和__delete__方法.property类实现了完整的描述符协议.通常,可以只实现部分协议,如只实现了__get__或__set__,而不必把__get__.__set__和__delete__全部实现 现在,让我们用描述符协议升级上一个章节Python动态属性和特性(二)的LineItem类 图1-1 我们将定义一个Quantity类,LineItem类会用到两个Quantit

Python属性描述符(二)

Python存取属性的方式特别不对等,通过实例读取属性时,通常返回的是实例中定义的属性,但如果实例未曾定义过该属性,就会获取类属性,而为实例的属性赋值时,通常会在实例中创建属性,而不会影响到类本身.这种不对等的方式对描述符类也有影响. def cls_name(obj_or_cls): # 传入一个实例,返回类名 cls = type(obj_or_cls) if cls is type: cls = obj_or_cls return cls.__name__.split('.')[-1] d

Python之路(十二):描述符,类装饰器,元类

python基础之面向对象(描述符.类装饰器及元类) 描述符 描述符(__get__,__set__,__delete__)   # 这里着重描述了python的底层实现原理 1. 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议. __get__():调用一个属性时,触发 __set__():为一个属性赋值时,触发 __delete__():采用del删除属性时,触发 1 class

python之属性描述符与属性查找规则

描述符 import numbers class IntgerField: def __get__(self, isinstance, owner): print('获取age') return self.num def __set__(self, instance, value): print('设置age值时') if not isinstance(value, numbers.Integral): raise ValueError('int need') self.num = value

python 描述符 上下文管理协议 类装饰器 property metaclass

1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性 描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件 注意事项: 一 描述符本身应该定义成新式类,被代理的类也应该是新式类 二