部分参考自: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

用@property装饰器指定一个getter方法,用@owner.setter装饰器指定了一个setter方法。当我们这么做的时候,访问owner属性,python就会自动调用相应的getter/setter方法。这样当我们要判断一个值的时候,如果放到__init__里,他只能在出初始化的时候判断,而放到setter里,每次set的时候都会判断。

可以把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。

__getter__,__setter__和类描述符都可以去掉重复的臃肿,实现内部代码的简洁。

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

  1. Python属性、方法和类管理系列之----描述符类

    什么是描述符类? 根据鸭子模型理论,只要具有__get__方法的类就是描述符类. 如果一个类中具有__get__和__set__两个方法,那么就是数据描述符,. 如果一个类中只有__get__方法,那 ...

  2. python基础-abstractmethod、__属性、property、setter、deleter、classmethod、staticmethod

    python基础-abstractmethod.__属性.property.setter.deleter.classmethod.staticmethod

  3. Python 面向对象之一 类与属性

    Python 面向对象之 类与属性 今天接触了一下面向对象,发现面向对象和之前理解的简直就是天壤之别,在学Linux的时候,一切皆文件,现在学面向对象了,so,一切皆对象. 之前不是一直在学的用面向函 ...

  4. 小甲鱼Python笔记(类)

    类和对象 类的构造方法 def __init__(): 1 class People: 2 def __init__(self,name): 3 self.name = name 注意:在构造方法中的 ...

  5. Python笔记_类

    1.类 1.1 类的定义 # 类的定义 class 类名: pass # pass是空语句,不具有任何意义,仅为了保持程序结构完整性 # 创建对象 对象名 = 类名() 1.2 成员变量 # 成员变量 ...

  6. 如何正确地使用Python的属性和描述符

    关于@property装饰器 在Python中我们使用@property装饰器来把对函数的调用伪装成对属性的访问. 那么为什么要这样做呢?因为@property让我们将自定义的代码同变量的访问/设定联 ...

  7. Python中属性和描述符的简单使用

    Python的描述符和属性是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题苦恼的 ...

  8. USB学习小记-HID类键盘的报告描述符的理解

    前言 断断续续的学习了将近三个月,才把USB的HID类搞明白,速度真是够慢的.利用晚上+周末的时间学习自己的东西确实是必要的,不过效率是有点低,以后要更专注一些才行,希望自己能做到吧. 在学习过程中, ...

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

    在类中,self只能在函数中使用,表示的是实例属性,就是每个实例可以设置不值,而不相互影响.如果在类级别使用没有self的属性,是类属性,一般作为全局变量来用的.事实上:就是一个是类属性 一个是对象属 ...

随机推荐

  1. iOS 字典与JSON相互转换

    iOS 字典与JSON相互转换 首先简单说一下为什么会写这种幼稚的文章. 现在的网络请求几乎都是AFN完成的,AFN也为我们写了了JSON转换字典的方法,但是不要忘记后台是一个很爱用JSON的人群,H ...

  2. android去掉滑动到顶部和底部的阴影

    android去掉滑动到顶部和底部的阴影 <ListView android:id="@+id/listView" android:layout_width="ma ...

  3. linux版基金看板

    程序员的吊丝们,还在害怕上班时偷偷看基金被老板发现吗?今天你们的福利来了,专属程序员吊丝一族的礼物,linux版基金看板. 优点: 1.自定义设置关注基金 2.linux系统,让别人可以以为你一直都在 ...

  4. ant+findbugs 扫描代码生成报告

    1. 下载安装ant.findbugs 下载ant.findbugs最新压缩包解压到本地磁盘合适位置,比如: D:\Program Files\apache-ant-1.9.7 D:\Program ...

  5. RMAN备份脚本一列分享

    在ORACLE数据库中,RMAN备份的脚本非常多,下面介绍一例shell脚本如何通过RMAN备份,以及FTP上传RMAN备份文件以及归档日志文件的脚本. fullback.sh 里面调用RMAN命令做 ...

  6. mysql unrecognized service问题解决

      在centos下用yum install mysql 安装完后,却发现用service mysqld start无法开启 出现mysqld:unrecognized service,网上别人说用/ ...

  7. kmeans算法c语言实现,能对不同维度的数据进行聚类

    最近在苦于思考kmeans算法的MPI并行化,花了两天的时间把该算法看懂和实现了串行版. 聚类问题就是给定一个元素集合V,其中每个元素具有d个可观察属性,使用某种算法将V划分成k个子集,要求每个子集内 ...

  8. char_dev.c 添加中文注释

    char_dev.c里的中文注释,仅代表个人理解,仅供参考.如有错误之处,请指出,谢谢! /* * linux/fs/char_dev.c * * Copyright (C) 1991, 1992 L ...

  9. Mac OS X常用操作入门指南

    前两天入手一个Macbook air,在装软件过程中摸索了一些基本操作,现就常用操作进行总结, 1关于触控板: 按下(不区分左右)            =鼠标左键 control+按下        ...

  10. C/S打包 客户端/windows程序 InstallShield

    开发完成后,我们可以直接在Debug目录下启动exe,运行程序. 但是,要如何安装,添加到开始菜单.桌面快捷方式.任务栏等.如Office安装效果: 开始菜单: 快捷菜单: 打包步骤: 1.下载Ins ...