python面向对象-2深入类的属性

在交互式环境中输入:

 1 >>> class A:
 2     a=0
 3     def __init__(self):
 4         self.a=10
 5         self.b=100
 6
 7
 8 >>> a=A()
 9 >>> a.a
10 10
11 >>> a.b
12 100
13 >>> A.a
14 0
15 >>> A.b
16 Traceback (most recent call last):
17   File "<pyshell#10>", line 1, in <module>
18     A.b
19 AttributeError: type object ‘A‘ has no attribute ‘b‘
20 >>> 

如下图:



还是在交互式环境中:

 1 >>> class A:
 2     a=0
 3     def __init__(self):
 4         self.a=10
 5         self.b=100
 6
 7
 8 >>> a=A()
 9 >>> getattr(a,‘a‘)#用getattr()函数获取实例a中a属性的值
10 10
11 >>> setattr(a,‘a‘,20)#设置实例a中a属性的值为20
12 >>> getattr(a,‘a‘)#用getattr()函数获取实例a中a属性的值
13 20
14 >>> hasattr(a,‘b‘)#测试实例a中是否包含属性b
15 True

图片展示:

这种反射机制的用字符串来操作类的属性和方法的三个函数并不常用。编写框架等特殊项目是采用到。



 1 class Washer:
 2
 3     def __init__(self,water=10,scour=2):
 4         self._water=water #不想让用户直接访问实例变量,可以标志成私有
 5         self.scour=scour
 6         #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法
 7     @property
 8     def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性
 9         return self._water
10
11     def set_water(self,water):
12         self.water=water
13
14     def set_scour(self,scour):
15         self.scour=scour
16
17     def add_water(self):
18         print(‘Add water:‘,self.water)
19
20     def add_scour(self):
21         print(‘Add scour:‘,self.scour)
22
23     def start_wash(self):
24         self.add_water()
25         self.add_scour()
26         print(‘Start wash...‘)
27
28 if __name__==‘__main__‘:
29     w=Washer()
30     #w.start_wash()
31     print(w.water)# 可以像访问属性一样访问方法

但这时用户仍然可以通过w._water来访问实例属性,封装的不好,也不会自动检查数据是不是浮点型,不好。

怎么解决?

用@属性.setter

 1 class Washer:
 2
 3     def __init__(self,water=10,scour=2):
 4         self._water=water #不想让用户直接访问实例变量,可以标志成私有
 5         self.scour=scour
 6         #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法
 7     @property
 8     def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性
 9         return self._water
10
11     @water.setter   #新添加代码
12     def water(self,water):
13         if 0<water<=500:
14             self._water=water
15         else:
16             print(‘set Failure!‘)
17
18     def set_water(self,water):
19         self.water=water
20
21     def set_scour(self,scour):
22         self.scour=scour
23
24     def add_water(self):
25         print(‘Add water:‘,self.water)
26
27     def add_scour(self):
28         print(‘Add scour:‘,self.scour)
29
30     def start_wash(self):
31         self.add_water()
32         self.add_scour()
33         print(‘Start wash...‘)
34
35 if __name__==‘__main__‘:
36     w=Washer()
37     print(w.water)# 可以像访问属性一样访问方法
38     #w._water=20 #为了不让用户这样直接给实例属性赋值,用下面的语句
39     w.water=123 #可以像给属性赋值一样给方法赋值,并检测范围
40     print(w.water)# 可以像访问属性一样访问方法

结果:

另外,很好奇@water.setter这个water到底是个什么东西,发现可以理解成一个新的实例属性,跟构造函数的形参没有关系。比如下图

再比如:

结果仍为:

同样可以包装一个删除变量,@water.delete

-------------------------------------

这块代码表示water这个变量可以重写,

------------------------------------------

上面这块代码表示water属性可以读取。

---------------------------------------------

最后一个用法是用属性装饰器@property来新定义一个虚拟的属性。

 1 class Washer:
 2
 3     def __init__(self,water=10,scour=2):
 4         self._water=water #不想让用户直接访问实例变量,可以标志成私有
 5         self.scour=scour
 6         self.year=2000#这是生产日期
 7         #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法
 8     @property
 9     def water1(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性
10         return self._water
11
12     @water1.setter
13     def water1(self,water):
14         if 0<water<=500:
15             self._water=water
16         else:
17             print(‘set Failure!‘)
18     @property
19     def total_year(self): #定义一个虚拟实例属性,这个属性其实是一个方法,但是可以按照属性来用
20         return 2017-self.year
21
22     def set_water(self,water):
23         self.water=water
24
25     def set_scour(self,scour):
26         self.scour=scour
27
28     def add_water(self):
29         print(‘Add water:‘,self.water)
30
31     def add_scour(self):
32         print(‘Add scour:‘,self.scour)
33
34     def start_wash(self):
35         self.add_water()
36         self.add_scour()
37         print(‘Start wash...‘)
38
39 if __name__==‘__main__‘:
40     w=Washer()
41     print(w.water1)# 可以像访问属性一样访问方法
42     #w._water=20 #为了不让用户这样直接给实例属性赋值,用下面的语句
43     w.water1=123
44     print(w.water1)# 可以像访问属性一样访问方法
45     print(w.total_year)
46
47     

运行结果:



描述符

描述符的意义是避免重复写具有相同限定属性的实例属性的定义代码。比如下面的例子:

 1 class NonNeg:#数据描述符
 2     def __init__(self,default=0):#构造方法
 3         self.default=default#一个实例属性
 4     def __get__(self,instance,owner):#协议方法
 5         return self.default
 6     def __set__(self,instance,val):#协议方法
 7         if val>0:
 8             self.default=val
 9         else:
10             print(‘The value must be NonNegative!‘)
11     def __delete__(self,instance):#协议方法
12         pass
13 class Movie:
14     rating=NonNeg()#描述符类NonNeg作另一个类Movie的属性,rating是Movie的类属性。
15     score=NonNeg()
16
17 if __name__==‘__main__‘:
18     m=Movie()
19     print(‘rating:‘,m.rating)
20     print(‘score:‘,m.score)#输出默认值default
21     m.rating=80#使用__set__协议方法
22     print(‘rating:‘,m.rating)#使用到 __get__协议方法
23     m.score=-3
24     print(‘score:‘,m.score)

输出结果:

---------------------------------------

下面说明所有的 类成员函数都是非数据描述符。

在这个交互式环境中可以看出pr这个类方法仅有__get__协议,三个不全,所以是非数据描述符。

----------------------------------------------------------------

同名的实例属性和非数据描述符(以类方法为例)同时出现时,访问的优先级是什么?

再看:

为啥结果还不一样了?再做一遍老师的例子:

重新打开idel之后重新写了一遍:

总结如下:

在交互式环境中,

若在类内实例方法中定义与此方法名想同的实例变量pr,则在类外实例化此类后,实例.pr 首先访问的是此实例变量,实例.pr() 肯定访问的是类内实例方法。若再类外实例中定义一个  实例.pr=20,则再访问 实例.pr时则访问的是刚定义的实例属性 实例.pr=20。

若在类内没有定义与类方法同名的实例属性,则实例.pr访问的是类内的实例方法,若又在类实例化后实例下定义同名的的实例属性pr,则 实例.pr访问的刚定义的。。。

感觉好混乱:若访问过t.pr()再访问t.pr,t.pr就为10了,若没有访问过t.pr()直接访问t.pr,这个就先访问的是method Tst.pr of <__main__.Tst object,也就是一个方法了。

 1 class Tst:
 2     def pr(self):
 3         self.pr=10
 4         print(‘Tst‘)
 5 t1=Tst()
 6 t1.pr()#输出Tst
 7 t1.pr#啥都没有输出
 8 print(t1.pr)#输出10
 9 print(‘下面实例化后不访问t.pr()直接访问t.pr:‘)
10 t2=Tst()
11 t2.pr#啥都没输出
12 print(t2.pr)#输出了bound method Tst.pr of <__main__.Tst object

但后来在实例下新定义的同名实例属性会覆盖原先类中定义的实例方法。优先级知道了吧。



扩展:

 1 class Tst:
 2     def __init__(self,default=1):
 3         self.water=default
 4     def __call__(self):
 5         print(‘包含call函数的类,他的实例可以直接当做函数使用。‘)
 6     def info(self):
 7         print("pass")
 8
 9 t=Tst()
10 t()

当调用t()时只调用类中__call__函数。

--------------------------------------------



解答如下:

 1 class surfaceNum:#定义一个描述类
 2     def __init__(self,default=1):
 3         self.number=default
 4     def __get__(self,instance,owner):#参数instance和owner暂时没有用到,只有self是固定名参数
 5         return self.number
 6     def __set__(self,instance,val):#参数instance暂时没有用到
 7         if 0<val<7 and isinstance(val,int)==True:
 8             self.number=val
 9             Box.info_num(self)#Box类还没有创建,故不能引用Box.infor_num,哈哈,能创建啊
10         else:
11             print(‘please set the correct surface number!‘)
12     def __delete__(self,instance):#协议方法
13          pass
14
15 class Box:#定义一个类名为Box,类名后不必有括号,类包含类属性和类方法,这个类没有定义类属性
16     ‘‘‘这是一个计算体积的类‘‘‘#这是这个类的__doc__属性,执行类后就可以在交互界面输入Box.__doc__查看这行说明文字了
17     openstate=0
18     number=surfaceNum()
19     def __init__(self):#这是类的构造函数,当实例化Box后会自动调用这个__init__方法
20         self.length=0.0 #这是实例属性,在类内访问用self.length,在类外访问用  实例名.length
21         self.width=0.0
22         self.height=0.0
23         self._color=‘red‘
24         self.__valum=0.0#双下换线开头的变量表示私有变量,所以他为私有实例属性,只能在类内访问到
25
26     @property
27     def color(self):
28         return self._color
29     @color.setter
30     def color(self,color):
31         self._color=color
32
33     def set_color(self,color):
34         self._color=color
35
36     def computevalum(self):#定义了一个类方法。
37         self.__valum=self.length*self.width*self.height
38         print(‘长度=‘,self.length,‘宽度=‘,self.width,‘高度=‘,self.height,‘valum=‘,self.__valum)
39
40     def info_color(self):
41         #self.set_color(self._color)#在类中,函数调用函数的方式
42         print(‘Box的颜色为‘,self._color)
43
44     def open_box(self):
45         if Box.openstate==0:
46             print(‘打开了Box‘)
47             Box.openstate=1
48         else:
49             print(‘Box已经打开了,不能重复打开‘)
50     def info_num(self):
51         #self.set_color(self._color)#在类中,函数调用函数的方式
52
53         print(‘Box面上的数字为‘,Box.number)
54     #定义 __call__  函数,输出体积
55     def __call__(self):
56         self.__valum=self.length*self.width*self.height
57         print(‘长度=‘,self.length,‘宽度=‘,self.width,‘高度=‘,self.height,‘调用自身computa()输出:valum=‘,self.__valum)
58
59
60
61
62 if __name__==‘__main__‘:
63     computa=Box() #实例化Box类
64     computa.number =2
65     computa.info_num()
66     computa.length=1
67     computa.width=2
68     computa.height=3
69     computa.computevalum()
70     computa()#实例名函数调用__call__函数直接输出体积
71     computa.set_color (‘yellow‘)
72     computa.info_color()
73     computa.open_box()
74     computa.color=‘green‘
75     computa.info_color()
76     print(‘‘)
77
78     computb=Box()#实例化Box类
79     computb.length=2
80     computb.width=2
81     computb.height=3
82     computb.computevalum()
83     computb.set_color (‘black‘)
84     computb.info_color()
85     computb.open_box()

这个题目是上节课题目的拔高,上节课题目及解答见链接http://www.cnblogs.com/zhubinglong/p/6942289.html

时间: 2024-11-18 23:21:38

python面向对象-2深入类的属性的相关文章

python面向对象程序设计(类成员)第二节

python 面向对象程序设计(类成员) 目录: (1)        类成员和实例成员 (2)        公有成员和私有成员 (3)        方法 (一)类成员与实例成员: 实例属性属于实例(对象),只能通过对象名访问. 类属性属于类,类名或对象名都可以访问,属于类的数据成员是在类中所有方法之外定义的. class Car:     price = 1000   #类属性     def __init__(self,c):         self.color = c car1 =

Python面向对象 --- 新旧式类、私有方法、类属性和类方法、静态方法

一.Python面向对象中的新旧式类 1)新式类(推荐使用):在定义类时,类后边括号里要继承基类(object).在python3.x中若没有指定父类,会默认使用的是object作为基类:在python2.x中,若没指定父类,则不会以object作为基类. 2)旧式类(经典类):在定义类时,类后边括号中不用继承object类,甚至不用括号. 3)dir方法是用来查看类的内置方法. 二.私有方法和属性 1)私有属性是对象不希望公开的属性:私有方法是对象不希望公开的方法.在定义私有属性和方法时,在属

python 面向对象整理 --------1.类和实例的属性

1.类的属性: # /usr/bin/env # coding = utf-8 class Chinese: #类的属性在这 country = 'China' def __init__(self, name): #实例的属性在这 self.name = name def play_ball(self, ball): print('%s正在打%s' % (self.name, ball)) #查看属性 print(Chinese.country) #修改属性 Chinese.country='J

Python面向对象编程、类

一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一个功能.举个例子,你要做饭,可以用电磁炉,也可以用煤气灶.不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路,两种最重要的编程范式分别是面向过程编程和面向对象编程. 提到面向对象,就不得不提到另一种编程思想,面向过程:什么是面向过程呢,面向过程的思想是把一个项目.一件事情按照一定的顺

Python -- 面向对象编程、类和实例、访问限制

面向对象编程 Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行.为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度. 而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之

Python面向对象进阶及类成员

再次了解多继承 先来一段代码 #!/usr/bin/env python # _*_ coding:utf-8 _*_ class A:    def bar(self):        print("BAR")        self.f1() class B(A):    def f1(self):        print("B") class C:    def f1(self):        print("C") class D(C,

Python面向对象静态方法,类方法,属性方法

静态方法(staticmethod名义上归类管理,实际上在静态方法里访问不到类或实例中的静态属性) 1 class days(object): 2 def __init__(self, food): 3 self.food = food 4 5 @staticmethod # 实际和类没有关系 6 def tell(self): 7 print('这里有%s,%s快来' % (self.food, 'name')) 8 9 10 a = days('香蕉') 11 a.tell(a) 类方法(c

Python面向对象编程——一些类定义(杂)

一.abstractmethod 子类必须全部实现重写父类的abstractmethod方法 非abstractmethod方法可以不实现重写 带abstractmethod方法的类不能实例化 from abc import abstractmethod, ABCMeta 1 class BettingStrategy(metaclass=ABCMeta): 2 3 @abstractmethod 4 def bet(self): 5 print('0') 6 7 def record_win(

python 面向对象整理 --------2.类的继承

1.类的继承: #/usr/bin/env #coding = utf-8 class animal(): def __init__(self,name): self.name=name def eat(self): print("%s eat food!" %self.name) class cat(animal): def cry(self): print("%s miaomiao jiao" %self.name) class dog(animal): def