一.定义
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
1 import math 2 class Circle: 3 def __init__(self,radius): #圆的半径radius 4 self.radius=radius 5 6 @property #area=property(area) 7 def area(self): 8 return math.pi * self.radius**2 #计算面积 9 10 @property 11 def perimeter(self): 12 return 2*math.pi*self.radius #计算周长 13 14 c=Circle(10) 15 #print(c.area()) 正常这么写 16 #print(c.perimeter()) 17 print(c.radius) 18 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 19 print(c.perimeter) #同上 20 ''' 21 输出结果: 22 10 23 314.1592653589793 24 62.83185307179586 25 '''
c.radius=10
c.radius=12
直接计算c.area c.perimeter
好处:
1.将函数伪装成数据属性,把数据逻辑伪装起来。
2.让身高,体重不断变的定义函数只用输入改变的体重,身高值得到BMI值。
注意:此时的特性arear和perimeter不能被赋值
c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute '''
ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
1.通过接口访问
1 class People: 2 def __init__(self,name): 3 self.__Name=name 4 def tell_name(self): #正常的话通过设定接口访问 5 return self.__Name 6 7 p1=People('cobila') 8 #print(p1._People__Name) 只能访问这个 9 print(p1.tell_name())
2.通过property访问
1 class People: 2 def __init__(self,name): 3 self.__Name=name #self.__Name 4 @property 5 def name(self): 6 return self.__Name 7 8 p1=People('cobila') 9 #print(p1._People__Name) 只能访问这个 10 print(p1.name) #让外面以为访问的name,其实是self.__Name
###不能做p1.name='alex'的赋值操作,ok
设置属性setter
被property修饰的属性会优于对象的属性被使用
而被propery装饰的属性,如sex,分为三种:
1.property
2.sex.setter
3.sex.deleter
1 class People: 2 def __init__(self,name,sex): 3 self.__Name=name 4 self.__sex=sex 5 @property 6 def sex(self): #2.回到定义 7 return self.__sex #3.取到真实值 8 @sex.setter 9 def sex(self,value): 10 print(self,value) 11 self.__sex=value #1.设置属性 12 13 p1=People('cobila','male') 14 print(p1.sex) 15 p1.sex='female' 16 print(p1.sex) 17 18 male 19 <__main__.People object at 0x0000000000B618D0> female 20 female
设定类型限制(Python没有类型限制,只能自己加)
1 class People: 2 def __init__(self,name,sex): 3 self.__Name=name 4 self.__sex=sex ##初始化操作的,不受下面伪装的property--setter影响(初始化 p1=People( 'cobila',1234) )也ok的) 但是修改这个再次赋值就会报错了 5 @property 6 def sex(self): #2.回到定义 7 return self.__sex #3.取到真实值 8 @sex.setter 9 def sex(self,value): 10 #print(self,value) 11 if not isinstance(value,str): 12 raise TypeError('性别必须是字符串类型') 13 self.__sex=value #1.设置属性 14 15 p1=People('cobila','male') 16 p1.sex='female' 17 print(p1.sex) 18 p1.sex=123123 19 20 21 raise TypeError('性别必须是字符串类型') 22 TypeError: 性别必须是字符串类型
删除
1 class People: 2 def __init__(self,name,sex): 3 self.name=name 4 self.__sex=sex 5 #self.sex=sex 6 @property 7 def sex(self): 8 return self.__sex 9 @sex.setter 10 def sex(self,value): 11 #print(self,value) 12 if not isinstance(value,str): 13 raise TypeError('性别必须是字符串类型') 14 self.__sex=value 15 @sex.deleter 16 def sex(self): 17 del self.__sex 18 19 # p1=People('cobila','male') 20 # p1.sex='female' 21 22 p1=People('cobila','male') 23 del p1.sex #相当于del self.__sex 24 print(p1.sex)
二.静态方法和类方法
1.静态方法
类的工具包,专门给类用的
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错,后续将介绍。
基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是
class Foo: @staticmethod #装饰器 def spam(x,y,z): print(x,y,z)
使用演示
print(type(Foo.spam)) #类型本质就是函数 Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数 f1=Foo() f1.spam(3,3,3) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制 ''' <class 'function'> 1 2 3 3 3 3 '''
1 import time 2 class Date: 3 def __init__(self,year,month,day): 4 self.year=year 5 self.month=month 6 self.day=day 7 @staticmethod 8 def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 9 t=time.localtime() #获取结构化的时间格式 10 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 11 @staticmethod 12 def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 13 t=time.localtime(time.time()+86400) 14 return Date(t.tm_year,t.tm_mon,t.tm_mday) 15 16 a=Date('2019',11,27) #自己定义时间 17 b=Date.now() #采用当前时间 调类下的now 18 c=Date.tomorrow() #采用明天的时间 19 20 print(a.year,a.month,a.day) 21 print(b.year,b.month,b.day) 22 print(c.year,c.month,c.day) 23 24 25 2019 11 27 26 2017 4 21 27 2017 4 22
但凡是定义在类的内部,并且没有被任何装饰器修饰过的方法,都是绑定方法,有自动传值功能。
1 d1=Date(1212,22,22) 2 print(d1.now) 3 print(Date.now) 4 #
#但凡定义在类的内部,并且被staticmethod装饰器修饰过的方法方法,都是解除绑定的方法,实际上就是函数,没有自动传值的功能了。 5 d_n1=Date.now() 6 d_n2=d1.now() 7 8 9 10 11 <function Date.now at 0x0000000000D3E2F0> 12 <function Date.now at 0x0000000000D3E2F0>
之前是类调的函数属性,对象调的函数方法
加上装饰器后,解除绑定,都是函数属性
2.类方法
1 class Foo: 2 def bar(self): 3 pass 4 @classmethod #把一个方法绑定给类,自动把对象本身当成一个参数传进去: 类.绑定到类的方法(),会把类本身当成第一个参数,自动传给绑定到类的方法 5 def test(cls,x): 6 print(cls,x) #拿掉一个类的内存地址后,就可以实例化或者引用类的属性 7 print(Foo.bar) #绑定给对象 8 print(Foo.test) #绑定给类 9 f=Foo() 10 print(f.bar) #绑定给foo下的对象f 11 12 13 14 <function Foo.bar at 0x00000000006BE268> 15 <bound method Foo.test of <class '__main__.Foo'>> 16 <bound method Foo.bar of <__main__.Foo object at 0x00000000006C1908>>
__str__(self)
__str__定义在类内部必须返回的字符串类型,
什么时候回触发它的执行呢?打印由这个类产生的对象时,会触发执行
1 class People: 2 def __init__(self,name,age): 3 self.name=name 4 self.age=age 5 def __str__(self): 6 return '<name: %s,age:%s>' %(self.name,self.age) 7 8 p1=People('egon',18) 9 print(p1) 10 11 12 13 <name: egon,age:18>
1 import time 2 class Date: 3 def __init__(self,year,month,day): 4 self.year=year 5 self.month=month 6 self.day=day 7 @classmethod 8 def now(cls): #用Date.now()的形式去产生实例,该实例用的是当前时间 9 print(cls) 10 t=time.localtime() #获取结构化的时间格式 11 obj=cls(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 12 return obj 13 @classmethod 14 def tomorrow(cls):#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 15 16 t=time.localtime(time.time()+86400) 17 return cls(t.tm_year,t.tm_mon,t.tm_mday) 18 19 class EuroDate(Date): 20 def __str__(self): 21 return '年:%s,月:%s,日:%s' %(self.year,self.month,self.day) 22 23 e1=EuroDate.now() 24 print(e1) 25 26 27 28 <class '__main__.EuroDate'> 29 年:2017,月:4,日:21