#动态语言允许在定义完类并创建类的属性之后,可以给该实例绑定任何属性和方法
#定义class
class Student(object):
pass
#创建实例
s=Student()
#绑定属性
s.name='Michale'
print(s.name) #Michale
#绑定方法
def set_age(self,age):
self.age=age
from types import MethodType
s.set_age=MethodType(set_age,s)#将方法绑定到对象s上
s.set_age(24)
print(s.age) #24
#-------------------------------------------------------------------------------------------------------#
#但是,给一个实例绑定的方法,对另一个实例是不起作用的,为了给所有实例都绑定方法,可以给class绑定方法
s2=Student()
#s2.set_age(34)
#print(s2.age) #AttributeError: 'Student' object has no attribute 'set_age'
#----------------------------------------比较两种绑定方法------------------------------------------#
def set_score(self,score):
self.score=score
flag=False
if flag==True:
Student.set_score=set_score #-----(1)这种绑定方法将属性绑定给每个实例,但是没有绑定到类上
s.set_score(100)
print(s.score) #100
s2.set_score(58)
print(s.score,s2.score) # 100 58
#print(Student.score) #AttributeError: type object 'Student' has no attribute 'score'
#--------(2)这种绑定方法将属性绑定给每个实例,同时也绑定到类上,并且保存的是最后一次的值
else:
Student.set_score=MethodType(set_score,Student)
s.set_score(100)
print(s.score) #100
s2.set_score(58)
print(s.score,s2.score) # 58 58
print(Student.score) #58--------(2)---------
#--------第(2)种绑定方法中,类属性是公共属性,所有实例都可以引用的,前提是实例自身没有同名的属性,
#因此类属性不能随意更改(别的实例可能在引用类属性的值)
def set_age(self,age):
self.age=age
class Stu(object):
pass
s=Stu()
a=Stu()
from types import MethodType
Stu.set_age=MethodType(set_age,Stu) #将方法绑定到类上
a.set_age(15) #通过set_age方法,设置的类属性age的值
s.set_age(11) #也是设置类属性age的值,并把上个值覆盖掉
print(s.age,a.age) #由于a和s自身没有age属性,所以打印的是类属性age的值 11 11
a.age = 10 #给实例a添加一个属性age并赋值为10
s.age = 20 #给实例b添加一个属性age并赋值为20
#这两个分别是实例a和s自身的属性,仅仅是与类属性age同名,并没有任何关系
print(s.age,a.age) #打印的是a和s自身的age属性值,不是类age属性值 20 10
#-------------------------------------------------------------------------------------------------------#
#--------如果我们想要限制实例的属性,例如,Student只能有属性name和age
#Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性(注意,此处是实例属性)
class Student2(object):
__slots__=('name','age') # 用tuple定义允许绑定的属性名称
s=Student2()
s.name='Michale'
print(s.name) #Michale
s.age=34
print(s.age) #34
#s.score=45
#print(s.score) #AttributeError: 'Student2' object has no attribute 'score'
#------------------slots不能限制类属性的添加------------------#
def set_city(self, city):
self.city=city
class Student3(object):
__slots__ = ('name', 'age', 'set_city')
pass
#--------(1)添加实例属性 不可以-------#
if flag==True:
Student3.set_city=set_city
b = Student3()
b.set_city('Beijing')
print(b.city) #AttributeError: 'Student3' object has no attribute 'city'
#--------(2)添加类属性 可以-------#
else:
Student3.set_city = MethodType(set_city, Student3)
a = Student3()
a.set_city('Beijing')
print(a.city) #Beijing
#-------------------------------------------------------------------------------------------------------#
#-------使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
class GraduateStudent(Student2):
pass
g=GraduateStudent()
g.score=56
print(g.score) #56
#除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
class GraduateStudent(Student2):
__slots__=('address')
g=GraduateStudent()
#g.score=56
#print(g.score) #AttributeError: 'GraduateStudent' object has no attribute 'score'
g.address='Beijing'
print(g.address) #Beijing
#--------------------------------------------@property---------------------------------------------------#
#在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改
#为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩
#在set_score()方法里,就可以检查参数
class Student4(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s=Student4()
#s.set_score(9999)#ValueError: score must between 0 ~ 100!
s.set_score(60)
print(s.get_score()) #60
#---------既能够检查参数,又能够用类似属性这样简单的方式来访问类的变量
#---------@property装饰器就是负责把一个方法变成属性调用,同时还可以进行参数检查--------------#
class Student5(object):
@property
def score(self):
return self._score #注意:此处的变量名_score必须和函数名score不重复,可以是score1等~
@score.setter
def score(self,value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
#把一个getter方法变成属性,只需要加上@property就可以了
#此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值
s=Student5()
s.score=57
print(s.score) #57
#------还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性,如下的birth是可读可写的,age是只读的
class Student6(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self,value):
self._birth=value
@property
def age(self):
return 2017-self._birth
s=Student6()
s.birth=1992
print(s.birth) #1992
#s.age=45 #AttributeError: can't set attribute
print(s.age) #25
class Screen(object):
def isint(self,value):
if not isinstance(value,int):
raise ValueError('value must be an integer!')
if value<0:
raise ValueError('value must be an positive integer!')
@property
def width(self):
return self._width
@width.setter
def width(self,value):
self.isint(value)
self._width=value
@property
def height(self):
return self._height
@height.setter
def height(self,value):
self.isint(value)
self._height=value
@property
def resolution(self):
return self._height*self._width
scr=Screen()
scr.width=1024
scr.height=768
print(scr.width,scr.height,scr.resolution) #1024 768 786432