面向对象
类
1.类的组成 数据和函数,二者是类的属性
2.两个作用: 实例化 属性引用
属性引用:类名.属性(增删改查)
实例化: 类名加括号就是实例化,会自动出发__init__的运行,可以用它为每个实例创建自己的特征
1
2
3
4
|
类属性 特征(变量) 技能(函数) 对象属性:对象本身只有特征(变量) |
class Province: memo = "中国的23个省之一" #类的数据属性 def __init__(self,name,capital,leader): self.Name = name self.Capital = capital self.Leadder = leader def sport_meet(self): print('%s 正在开运动会'% self.Name) def pao(self,pao): print('%s 正在放%s炮'% (self.Name,pao)) -------------------类-------------------- print(Province.__dict__) #查看类属性字典 print(Province.memo) #查看类的数据属性 中国的23个省之一 print(Province.__dict__["memo"]) #查看类的数据属性 中国的23个省之一 Province.sport_meet("山西") #查看类的函数属性 正在开运动会 Province.__dict__["sport_meet"]("山西") #查看类的函数属性 正在开运动会 --------------------------------------- --------------类的其它属性------------------ 类名.__name__ # 类的名字(字符串) 类名.__doc__ # 类的文档字符串 类名.__base__ # 类的第一个父类(在讲继承时会讲) 类名.__bases__ # 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__ # 类的字典属性 类名.__module__ # 类定义所在的模块 类名.__class__ # 实例对应的类(仅新式类中) ------------------------------------------ # ----------------------对象-------------------- hb = Province('河北','石家庄','李杨') #创建实例 print(hb.__dict__) {'Capital': '石家庄', 'Leadder': '李杨', 'Name': '河北'} print(hb.memo) #实例调取类的数据属性 中国的23个省之一 hb.sport_meet() #实例调取没有参数的函数 河北 正在开运动会 hb.pao("大") #实例调取有参数的函数 河北 正在放大炮
1 class Chinese: 2 country="china" 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print("%s 正在打 %s" %(self.name,ball)) 8 9 p1=Chinese("alex") 10 11 #对象执行类的数据属性 12 # print(p1.country) 13 # china 14 15 16 #对象改p1.country="日本",相当于给对象的__dict__增加值,而不是改变类的数据属性 17 print(p1.__dict__) 18 # {'name': 'alex'} 19 p1.country="日本" 20 print(p1.__dict__) 21 # {'name': 'alex', 'country': '日本'} 22 print(p1.country) 23 # 日本 24 print(Chinese.country) 25 #china
1 #类里面的数据属性contry只会被Chinese.country和p1.country调用到 2 country = "日本" 3 4 class Chinese: 5 country = "china" #只会被类和对象调用到,带点的 6 7 def __init__(self,name): 8 self.name=name 9 print(country) #此时的country就是一个普通的变量 10 11 def play_ball(self,ball): 12 print("%s 正在打 %s" %(self.name,ball)) 13 14 p1=Chinese("alex") 15 #日本
1 #p1.l1 = [1,2,3]相当于给p1__dict__增加 2 # p1.l1.append(5)相当于给原来的l1追加,相当于操作的是类 3 class Chinese: 4 l1 = ["alex","egon"] 5 def __init__(self,name): 6 self.name=name 7 8 9 def play_ball(self,ball): 10 print("%s 正在打 %s" %(self.name,ball)) 11 12 p1=Chinese("alex") 13 14 15 p1.l1 = [1,2,3] 16 print(Chinese.l1) 17 #['alex', 'egon'] 18 19 #追加之后发现原来的l1也变了 20 p1.l1.append(5) 21 print(p1.l1) 22 #['alex', 'egon', 5] 23 print(Chinese.l1) 24 #['alex', 'egon', 5]
静态方法 staticmethod
#静态方法,此时对象调类里面的函数不是绑定方法,而是普通的函数 class Foo: @staticmethod def spam(x,y,z): #没有self,即使有self,也会当成普通参数 print(x,y,z) f2=Foo() f2.spam(1,2,3)
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 obj=Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 return obj #之前实例化 # d1=Date(2017,1,13) # print(d1.year,d1.month,d1.day) # 2017 1 13 #现在实例化,不需要传值 # data_now = Date.now() # print(data_now.year,data_now.month,data_now.day) # 2017 4 22
类方法 classmethod
把一个方法绑定给类:类.绑定到类的方法(),会把类本身当做第一个参数自动传给绑定到类的方法
class Foo: @classmethod #把一个方法绑定给类:类.绑定到类的方法(),会把类本身当做第一个参数自动传给绑定到类的方法 def test(cls,x): print(cls,x) #拿掉一个类的内存地址后,就可以实例化或者引用类的属性了 Foo.test(123) <class '__main__.Foo'> 123 f = Foo() print(f.test) # 发现现在对象调时候成了类的绑定方法 <bound method Foo.test of <class '__main__.Foo'>> f.test(123) #现在对象传值的时候第一个传的默认是类 <class '__main__.Foo'> 123
#__str__定义在类内部,必须返回一个字符串类型, #什么时候会触发它的执行呢?打印由这个类产生的对象时,会触发执行 class People: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return 'name:%s,age:%s' %(self.name,self.age) p1=People('egon',18) print(p1) import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @classmethod def now(cls): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 obj=cls(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 return obj class EuroDate(Date): def __str__(self): return '年:%s,月:%s,日:%s' %(self.year,self.month,self.day) e1=EuroDate.now() print(e1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
在类内部定义的函数无非三种用途 一:绑定到对象的方法 只要是在类内部定义的,并且没有被任何装饰器修饰过的方法,都是绑定到对象的 class Foo: def test( self ): #绑定到对象的方法 pass def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常 pass 绑定到对象,指的是:就给对象去用, 使用方式:对象.对象的绑定方法(),不用为 self 传值 特性:调用时会把对象本身当做第一个参数传给对象的绑定方法 二:绑定到类的方法: classmethod 在类内部定义的,并且被装饰器@ classmethod 修饰过的方法,都是绑定到类的 class Foo: def test( self ): #绑定到对象的方法 pass def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常 pass 绑定到对象,指的是:就给对象去用, 使用方式:对象.对象的绑定方法() 特性:调用时会把对象本身当做第一个参数传给对象的绑定方法 三:解除绑定的方法: staticmethod 既不与类绑定,也不与对象绑定,不与任何事物绑定 绑定的特性:自动传值(绑定到类的就是自动传类,绑定到对象的就自动传对象) 解除绑定的特性:不管是类还是对象来调用,都没有自动传值这么一说了 所以说 staticmethod 就是相当于一个普通的工具包 class Foo: def test1( self ): pass def test2(): pass @classmethod def test3( cls ): pass @classmethod def test4(): pass @staticmethod def test5(): pass test1与test2都是绑定到对象方法:调用时就是操作对象本身 <function Foo.test1 at 0x0000000000D8E488 > <function Foo.test2 at 0x0000000000D8E510 > test3与test4都是绑定到类的方法:调用时就是操作类本身 <bound method Foo.test3 of < class '__main__.Foo' >> <bound method Foo.test4 of < class '__main__.Foo' >> test5是不与任何事物绑定的:就是一个工具包,谁来都可以用,没说专门操作谁这么一说 <function Foo.test5 at 0x0000000000D8E6A8 > |
面向对象的三大特性
一.继承
继承
继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类。
python中类的继承分为:单继承和多继承
继承有两种用途:
1:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
2:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,
并且实现接口中的功能
1 class Animal: 2 3 def eat(self): 4 print "%s 吃 " %self.name 5 6 def drink(self): 7 print "%s 喝 " %self.name 8 9 def shit(self): 10 print "%s 拉 " %self.name 11 12 def pee(self): 13 print "%s 撒 " %self.name 14 15 16 class Cat(Animal): 17 18 def __init__(self, name): 19 self.name = name 20 self.breed = '猫' 21 22 def cry(self): 23 print '喵喵叫' 24 25 class Dog(Animal): 26 27 def __init__(self, name): 28 self.name = name 29 self.breed = '狗' 30 31 def cry(self): 32 print '汪汪叫' 33 34 35 # ######### 执行 ######### 36 37 c1 = Cat('小白家的小黑猫') 38 c1.eat() 39 40 c2 = Cat('小黑的小白猫') 41 c2.drink() 42 43 d1 = Dog('胖子家的小瘦狗') 44 d1.eat()
组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
class Teacher: def __init__(self,name,sex,course): self.name=name self.sex=sex self.course=course class Student: def __init__(self,name,sex,course): self.name=name self.sex=sex self.course=course class Course: def __init__(self,name,price,peroid): self.name=name self.price=price self.period=peroid python_obj=Course('python',15800,'7m') t1=Teacher('egon','male',python_obj) s1=Student('cobila','male',python_obj) print(s1.course.name) print(t1.course.name)
组合与继承
组合对比继承来说,也是用来重用代码,但是组合描述的是一种“有”的关系
都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
老师有课程 学生有成绩 学生有课程 学校有老师 学校有学生 class Course: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(name,course): self.name=name self.course=course class Student: def __init__(name,course): self.name=name self.course=course python=Course('python',15800,'7m') t1=Teacher('egon',python) s1=Student('alex',python) print(s1.course.name) print(s1.course.period)
派生
子类继承了父类的属性,然后衍生出自己新的属性,如果子类衍生出的新的属性与父类的某个属性名字相同,
那么再调用子类的这个属性,就以子类自己这里的为准了
class People: def __init__(self,name,sex,age): self.name=name self.age=age self.sex=sex def walk(self): print('%s is walking' %self.name) class Chinese(People): country='China' def __init__(self,name,sex,age,language='Chinese'): People.__init__(self,name,sex,age) self.language=language def walk(self): People.walk(self) class North_korean(People): country='Korean' c=Chinese('egon','male',18) print(c.name,c.age,c.sex) egon 18 male print(c.__dict__) {'sex': 'male', 'age': 18, 'language': 'Chinese', 'name': 'egon'} print(c.country) China c.walk() egon is walking
接口与归一化设计
1.接口
为何要用接口:
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。 def read(self): #定接口函数read pass def write(self): #定义接口函数write pass class Txt(Interface): #文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(Interface): #磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法')
2.抽象类
1 什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
2 为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
import abc #抽象类:本质还是类,与普通类额外的特点的是:加了装饰器的函数,子类必须实现他们 class Animal(metaclass=abc.ABCMeta): tag='123123123123123' @abc.abstractmethod def run(self): pass @abc.abstractmethod def speak(self): pass class People(Animal): def run(self): pass def speak(self): pass peo1=People() print(peo1.run)
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 #一切皆文件 4 import abc #利用abc模块实现抽象类 5 6 class All_file(metaclass=abc.ABCMeta): 7 all_type='file' 8 @abc.abstractmethod #定义抽象方法,无需实现功能 9 def read(self): 10 '子类必须定义读功能' 11 pass 12 13 @abc.abstractmethod #定义抽象方法,无需实现功能 14 def write(self): 15 '子类必须定义写功能' 16 pass 17 18 # class Txt(All_file): 19 # pass 20 # 21 # t1=Txt() #报错,子类没有定义抽象方法 22 23 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 24 def read(self): 25 print('文本数据的读取方法') 26 27 def write(self): 28 print('文本数据的读取方法') 29 30 class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 31 def read(self): 32 print('硬盘数据的读取方法') 33 34 def write(self): 35 print('硬盘数据的读取方法') 36 37 class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 38 def read(self): 39 print('进程数据的读取方法') 40 41 def write(self): 42 print('进程数据的读取方法') 43 44 wenbenwenjian=Txt() 45 46 yingpanwenjian=Sata() 47 48 jinchengwenjian=Process() 49 50 #这样大家都是被归一化了,也就是一切皆文件的思想 51 wenbenwenjian.read() 52 yingpanwenjian.write() 53 jinchengwenjian.read() 54 55 print(wenbenwenjian.all_type) 56 print(yingpanwenjian.all_type) 57 print(jinchengwenjian.all_type)
2 继承顺序原理(python如何实现的继承)
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
1 class A(object): 2 def test(self): 3 print('from A') 4 5 class B(A): 6 def test(self): 7 print('from B') 8 9 class C(A): 10 def test(self): 11 print('from C') 12 13 class D(B): 14 def test(self): 15 print('from D') 16 17 class E(C): 18 def test(self): 19 print('from E') 20 21 class F(D,E): 22 # def test(self): 23 # print('from F') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 28 29 #新式类继承顺序:F->D->B->E->C->A 30 #经典类继承顺序:F->D->B->A->E->C 31 #python3中统一都是新式类 32 #pyhon2中才分新式类与经典类
3.子类调用父类的方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
1 class Vehicle: #定义交通工具类 2 Country='China' 3 def __init__(self,name,speed,load,power): 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 9 def run(self): 10 print('开动啦...') 11 12 class Subway(Vehicle): #地铁 13 def __init__(self,name,speed,load,power,line): 14 Vehicle.__init__(self,name,speed,load,power) 15 self.line=line 16 17 def run(self): 18 print('地铁%s号线欢迎您' %self.line) 19 Vehicle.run(self) 20 21 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) 22 line13.run()
方法二:super()
super在python2中的用法: 1:super(自己的类,self).父类的函数名字 2:super只能用于新式类 class People(object): def __init__(self,name,sex,age): self.name=name self.age=age self.sex=sex def walk(self): print('%s is walking' %self.name) class Chinese(People): country='China' def __init__(self,name,sex,age,language='Chinese'): super(Chinese,self).__init__(name,sex,age) self.language=language c=Chinese('egon','male',18) print c.name,c.age,c.sex,c.language
class People: def __init__(self,name,sex,age): self.name=name self.age=age self.sex=sex def walk(self): print('%s is walking' %self.name) class Chinese(People): country='China' def __init__(self,name,sex,age,language='Chinese'): # self.name=name # self.sex=sex # self.age=age # People.__init__(self,name,sex,age) super(Chinese,self).__init__(name,sex,age) self.language=language def walk(self,x): super().walk() print('子类的x',x) c=Chinese('egon','male',18) print(c.name,c.age,c.sex,c.language) c.walk(123)
二.多态
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
1. 序列类型有多种形态:字符串,列表,元组。
2. 动物有多种形态:人,狗,猪
3. 文件有多种形态:文件文件,可执行文件
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao')
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
多态性
同一种调用方式,不同的调用效果
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
综上我们也可以说,多态性是‘一个接口(函数func)
class Animal: def run(self): raise AttributeError('子类必须实现这个方法') class People(Animal): def run(self): print('people is walking') class Pig(Animal): def run(self): print('pig is walking') class Dog(Animal): def run(self): print('dog is running') peo1=People() pig1=Pig() ------------------人和猪都有run方法--------- peo1.run() pig1.run() people is walking pig is walking -----------------多态性,函数把run方法包装------------ 多态性:定义统一的接口,可以传入不同类型的值,但是调用的逻辑都一样,执行的结果却不一样 def Run(name): name.run() Run(peo1) Run(pig1) people is walking pig is walking
三.封装
为什么要封装
封装数据的主要原因是:保护隐私(作为男人的你,脸上就写着:我喜欢男人,你害怕么?)
封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了
比如你不必知道你的鸟是怎出来的,你直接掏出自己的接口就能用尿这个功能)
封装的两个层面
1.第一个层面的封装:(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种 封装
2.第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问
class A: __x = 1 #转换为_A__x def __test(self): print("from A") print(A.__dict__) { '_A__x': 1 } ------查看类的隐藏属性———— obj = A() print(A._A__x) 1 print(obj._A__x) 1 A._A__test(123) from A obj._A__test() from A
特性(property)
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) -----------------------执行结果-------- 10 314.1592653589793 62.83185307179586
class People: def __init__(self,name,age,height,weight): self.name=name self.age=age self.height=height self.weight=weight @property def bodyindex(self): return self.weight/(self.height**2) p1=People('cobila',38,1.65,74) print(p1.bodyindex) p1.weight=200 print(p1.bodyindex) ------------------------结果--------- 27.180899908172638 73.46189164370983
class People: def __init__(self,name): self.__name = name @property def name(self): return self.__name p1 = People("egon") print(p1.name) #会找含有@property下name的函数 ----------------------------- egon
class People: def __init__(self,name,Sex): self.name = name self.__sex = Sex @property def sex(self): return self.__sex @sex.setter def sex(self,value): self.__sex = value p1 = People("egon","male") print(p1.sex) #会找含有@property下name的函数 p1.sex = "famale" #修改会找@sex.setter下的函数 print(p1.sex) --------------------- male famale #修改性别,判断性别如果不为字符串,就报错 class People: def __init__(self,name,Sex): self.name = name self.__sex = Sex @property def sex(self): return self.__sex @sex.setter def sex(self,value): if not isinstance(value,str): #如果性别不为字符串,就报错 raise TypeError("性别必须为字符串") self.__sex = value p1 = People("egon","male") p1.sex = 123 #修改会找@sex.setter下的函数 print(p1.sex) ----------------- TypeError: 性别必须为字符串 # 实例化时后,如果不是字符串,也报错 class People: def __init__(self,name,Sex): self.name = name self.sex = Sex #此时是self.sex,而不是self.__sex @property def sex(self): return self.sex @sex.setter def sex(self,value): if not isinstance(value,str): #如果性别不为字符串,就报错 raise TypeError("性别必须为字符串") self.__sex = value p1 = People("egon",111) #执行报错 ---------------- TypeError: 性别必须为字符串
# 实例化时,删除性别 class People: def __init__(self,name,Sex): self.name = name self.sex = Sex #此时是self.sex,而不是self.__sex #实例化self.sex时候,会执行下面的@sex.setter内容 @property def sex(self): return self.__sex @sex.setter def sex(self,value): if not isinstance(value,str): #如果性别不为字符串,就报错 raise TypeError("性别必须为字符串") self.__sex = value @sex.deleter def sex(self): del self.__sex p1 = People("egon","male") print(p1.sex) male del p1.sex #删除 print(p1.sex) 报错