一、面向对象初识
类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用,特征被称为属性,行为被称为方法,类就相当于制造飞机时的图纸,是一个模板,是负责创建对象的。
对象是由类创建出来的一个具体存在,可以直接使用,由哪个类创建出来的对象,就拥有在那个类中定义的属性和方法,对象就相当于用图纸制造的飞机,在程序开发中,先有类,然后再有对象。
1.类的结构
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 print('人类会工作') class 是关键字与def用法相同,定义一个类。 Human是此类的类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。 类的结构从大方向来说就分为两部分: 静态变量。 动态方法。
2.从类名的角度研究类
2.1 类名操作静态属性
1.通过类名.__dict__方式可以查看类中的所有内容。
class Human: """ 此类主要是构建人类 """ mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段 dic = {} l1 = [] def work(self): # 第二部分:方法 函数 动态属性 # print(self) print('人类会工作') print(Human.__dict__) print(Human.__dict__['mind']) Human.__dict__['mind'] = '无脑' print(Human.__dict__) # 错误 #通过这种方式只能查询全部内容,不能增删改,一般不用单独属性查询.
2.通过万能的.
class Human: """ 此类主要是构件人类 """ mind = "有思想" dic = {} l1 = [] def work(self): print("人类会工作") print(Human.mind) # 查 Human.mind = "无脑" # 改 print(Human.mind) del Human.mind # 删 # print(Human.mind) Human.walk = "直立行走" # 增加 print(Human.walk) # 通过万能的点 可以增删改查类中的单个属性
对以上两种做一个总结:如果想查询类中的所有内容,通过第一种__dict__,如果只是操作单个属性则用万能的点的方式。
2.2 类名操作动态方法
说明:除了两个特殊方法,静态方法和类方法,一般不会通过类名操作一个类中的方法
class Human: """ 此类主要是构件人类 """ mind = "有思想" dic = {} l1 = [] def work(self): print("人类会工作") def tools(self): print("人类会使用工具") Human.work(111) Human.tools(111) # 下面可以做,但不用 Human.__dict__["work"](111)
3.从对象的角度研究类
3.1什么是对象
对象是从类中出来的,只要是类名加上(),这就是一个实例化过程,这个就会实例化一个对象。
class Human: mind = "有思想" def __init__(self): print(666) print(self) def work(self): print("人类会工作") def tools(self): print("人类会使用工具") obj = Human() # 只要实例化对象,就会自动执行__init__方法 print(obj) # obj的地址和self地址相同
其实实例化一个对象总共发生了三件事:
- 1,在内存中开辟了一个对象空间。
- 2,自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
- 3,在__init__ 方法中通过self给对象空间添加属性。
3.2 对象操作对象空间属性
1.对象查询对象中所有属性,对象.__dict__
class Human: mind = "有思想" language = "实用语言" def __init__(self, name, age, hobby): self.name = name self.age = age self.hobby = hobby obj = Human("小名", 19, "唱歌") # 只要实例化对象,就会自动执行__init__方法 print(obj.__dict__) # {'name': '小名', 'age': 19, 'hobby': '唱歌'}
2.对象操作对象中的单个属性,通过点.
class Human: mind = "有思想" language = "实用语言" def __init__(self, name, age, hobby): self.name = name self.age = age self.hobby = hobby obj = Human("小名", 19, "唱歌") # 只要实例化对象,就会自动执行__init__方法 print(obj.name) # 查 obj.hobby = "跳舞" # 改 del obj.age # 删 obj.job = "IT" # 增 print(obj.__dict__) # {'name': '小名', 'hobby': '跳舞', 'job': 'IT'}
3.3 对象查看类中的属性
class Human: mind = "有思想" language = "实用语言" def __init__(self, name, age, hobby): self.name = name self.age = age self.hobby = hobby obj = Human("小名", 19, "唱歌") # 只要实例化对象,就会自动执行__init__方法 print(obj.mind) # 有思想 print(obj.language) # 实用语言
3.4 对象操作类中的方法
class Human: mind = "有思想" language = "实用语言" def __init__(self, name, age, hobby): self.name = name self.age = age self.hobby = hobby def work(self): print("人类会工作") def tools(self): print("人类会使用工具") obj = Human("小名", 19, "唱歌") # 只要实例化对象,就会自动执行__init__方法 obj.work() obj.tools()
类中的方法一般都是通过对象执行的(除去类方法,静态方法外),并且对象执行这些方法都会自动将对象空间传给方法中的第一个参数self。
说明:
- 1.Python中self表示当前对象,当前正在操作的对象,而不是当前类,当某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以我们只需要传递后面的参数即可。
- 2.当使用类名()创建对象时,会自动执行以下操作:为对象在内存中分配空间,即创建对象;为对象的属性设置初始值,即调用初始化方法,这个初始化方法就是__init__方法,该方法是对象的内置方法。__init__方法是专门用来定义一个类具有哪些属性的方法,在__init__方法内部使用self.属性名=属性的初始值,就可以定义属性,定义属性后,再使用类创建的对象,都会拥有该属性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("%s正在吃饭..." % self.name)
obj = Person("小名", "18")
obj.eat()
一个类可以实例化多个对象
class Human: mind = "有思想" language = "实用语言" def __init__(self, name, age, hobby): self.name = name self.age = age self.hobby = hobby def work(self): print("人类会工作") def tools(self): print("人类会使用工具") obj1 = Human("小名", 19, "唱歌") # 只要实例化对象,就会自动执行__init__方法 obj2 = Human("萧山", 29, "跳舞") print(obj1, obj2) # <__main__.Human object at 0x03B59388> <__main__.Human object at 0x03B59520> print(obj1.__dict__) # {'name': '小名', 'age': 19, 'hobby': '唱歌'} print(obj2.__dict__) # {'name': '萧山', 'age': 29, 'hobby': '跳舞'}
二、类空间问题及类之间关系
2.类的空间问题
2.1 何处可以添加对象属性
class A: def __init__(self, name): self.name = name def func(self, sex): self.sex = sex # 类外面可以: obj = A('barry') obj.age = 18 print(obj.__dict__) # {'name': 'barry', 'age': 18} # 类内部也可以: obj = A('barry') # __init__方法可以。 obj.func('男') # func 方法也可以。
总结:对象的属性不仅可以在__init__里面添加,还可以在类的其他方法或者类的外面添加。
2.2 何处可以添加类的静态属性
ef __init__(self,name): self.name = name def func(self,sex): self.sex = sex def func1(self): A.bbb = 'ccc'
# 类的外部可以添加 A.aaa = 'boxiaoyuan' print(A.__dict__) # 类的内部也可以添加。 A.func1(111) print(A.__dict__)
总结:类的属性不仅可以在类内部添加,还可以在类的外部添加。
2.3 对象如何找到类的属性
对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->.....
类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> ........
上面的顺序都是单向不可逆,类名不可能找到对象的属性。
3.类与类之间的关系
在⾯向对象的世界中,类与类中存在以下关系:
1. 依赖关系
2. 关联关系
3. 组合关系
4. 聚合关系
5. 实现关系
6. 继承关系(类的三大特性之一:继承。)
3.1 依赖关系
依赖关系:给一个类的方法传了一个参数此参数是另一个类的对象(类名)。比如: ⼤象和冰箱之间就是依赖关系. 这种关系就是兼职关系,我⽤着你. 但是你不属于我. 这种关系是最弱的。
class Elphant: def __init__(self, name): self.name = name def open(self, obj1): print("大象要开门") def close(self): print("大象要关门") class Refrigerator: def __init__(self, name): self.name = name def open_door(self): print("冰箱门被打开") def close_door(self): print("冰箱门被关闭") elphant = Elphant("大象") haier = Refrigerator("海尔") elphant.open(haier)
3.2 关联,聚合,组合关系
其实这三个在代码上写法是⼀样的. 但是, 从含义上是不⼀样的.
1. 关联关系. 两种事物必须是互相关联的. 但是在某些特殊情况下是可以更改和更换的.
2. 聚合关系. 属于关联关系中的⼀种特例. 侧重点是xxx和xxx聚合成xxx. 各⾃有各⾃的声明周期. 比如电脑. 电脑⾥有CPU, 硬盘, 内存等等. 电脑挂了. CPU还是好的. 还是完整的个体
3. 组合关系. 属于关联关系中的⼀种特例. 写法上差不多. 组合关系比聚合还要紧密. 比如⼈的⼤脑, ⼼脏, 各个器官. 这些器官组合成⼀个⼈. 这时. ⼈如果挂了. 其他的东⻄也跟着挂了
关联关系
将一个对象的属性赋值 修改为另一个对象
class Boy: def __init__(self, name, grilfriend=None): self.name = name self.grilfriend = grilfriend def have_a_dinner(self): if self.grilfriend: print('%s和%s一起共度晚餐' % (self.name, self.grilfriend.name)) else: print('吃海鲜') def add_grilfriend(self, gril): self.grilfriend = gril # 将一个对象的属性赋值 修改为另一个对象 def remove_grilfriend(self): self.grilfriend = None class Gril: def __init__(self, name): self.name = name b = Boy('rock') # b.have_a_dinner() g = Gril('小七') b.add_grilfriend(g) # 将一个对象的属性赋值 修改为另一个对象 # b.have_a_dinner() b.remove_grilfriend() b.have_a_dinner()
聚合关系
聚合关系在代码上体现为:类A由类B聚合而成,类A包含有类B的全局对象,但类B的对象可以不在类A创建的时刻创建。
class Cpu(object): def __init__(self): self.type = '286' class Computer(object): def __init__(self, cpu): self.cpu = cpu # 有一个CPu类的实例对象 def __del__(self): print ("没有权力和Cpu by by!") old_cpu = Cpu() old_computer = Computer(old_cpu) del old_computer
组合关系
class Cpu(object): def __init__(self): self.type = '286' class Computer(object): def __init__(self): self.cpu = Cpu() # 包含CPu类的实例对象 def __del__(self): print ("Cpu by by!") old_computer = Computer() del old_computer
三、继承
1.什么是面向对象继承
继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。
2.继承的分类
如:Aminal 叫做父类,基类,超类。Person Cat Dog: 子类,派生类。
继承:可以分单继承,多继承。
这里需要补充一下python中类的种类(继承需要):、
在python2x版本中存在两种类.:
⼀个叫经典类:在python2.2之前. ⼀直使⽤的是经典类,经典类在基类的根如果什么都不写;⼀个叫新式类.:在python2.2之后出现了新式类.,新式类的特点是基类的根是object类。python3x版本中只有一种类:python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object。
3.单继承
3.1 类名,对象执行父类方法
class Aniaml(object): type_name = '动物类' def __init__(self, name, sex, age): self.name = name self.age = age self.sex = sex def eat(self): print(self) print('吃东西') class Person(Aniaml): pass class Cat(Aniaml): pass class Dog(Aniaml): pass # 类名: print(Person.type_name) # 可以调用父类的属性,方法。 动物类 Person.eat(111) # 111 吃东西 # 对象: # 实例化对象 p1 = Person('春哥', '男', 18) print(p1.__dict__) # {'name': '春哥', 'age': 18, 'sex': '男'} # 对象执行类的父类的属性,方法。 print(p1.type_name) # 动物类 p1.type_name = '666' print(p1) # <__main__.Person object at 0x035C9490> p1.eat() # <__main__.Person object at 0x035C9490> 吃东西
3.2 执行顺序
class Aniaml(object): type_name = '动物类' def __init__(self, name, sex, age): self.name = name self.age = age self.sex = sex def eat(self): print(self) print('吃东西') class Person(Aniaml): def eat(self): print("%s 吃饭" % self.name) class Cat(Aniaml): pass class Dog(Aniaml): pass p1 = Person('barry', '男', 18) # 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。 p1.eat() # 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法
3.3 同时执行类以及父类方法
方法一:
如果想执行父类的func方法,这个方法并且子类中也用,那么就在子类的方法中写上:
父类.func(对象,其他参数)
class Aniaml(object): type_name = '动物类' def __init__(self, name, sex, age): self.name = name self.age = age self.sex = sex def eat(self): print('吃东西') class Person(Aniaml): def __init__(self, name, sex, age, mind): Aniaml.__init__(self, name, sex, age) # 方法一 self.mind = mind def eat(self): super().eat() print('%s 吃饭'%self.name) # 方法一: p1 = Person('春哥', 'laddboy', 18, '有思想') print(p1.__dict__) # {'name': '春哥', 'age': 18, 'sex': 'laddboy', 'mind': '有思想'}
方法二:
利用super,super().func(参数)
class Aniaml(object): type_name = '动物类' def __init__(self, name, sex, age): self.name = name self.age = age self.sex = sex def eat(self): print('吃东西') class Person(Aniaml): def __init__(self, name, sex, age, mind): super().__init__(name, sex, age) # 方法二 self.mind = mind def eat(self): super().eat() print('%s 吃饭'%self.name) # 方法二: p1 = Person('春哥', 'laddboy', 18, '有思想') print(p1.__dict__) # {'name': '春哥', 'age': 18, 'sex': 'laddboy', 'mind': '有思想'}
4. 多继承
class ShenXian: # 神仙 def fei(self): print("神仙都会⻜") class Monkey: # 猴 def chitao(self): print("猴⼦喜欢吃桃⼦") class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是⼀只猴 pass sxz = SunWukong() # 孙悟空 sxz.chitao() # 会吃桃⼦ sxz.fei() # 会⻜
这里需要补充一下python中类的种类(继承需要):
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写。
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object。
深度优先和广度优先是两种不同的算法策略,有什么区别呢?
如图,B 继承 A, C 继承 A, D 继承 B 和 C。
深度优先遍历是从D开始往上搜索到B,若B没有数据,则继续往上搜索到 A;
广度优先遍历是从 D 开始往上搜索到 B,若 B 没有数据,则搜索和 B 同级的 C 里的数据,若同级的 C 里还是没有数据,再继续往上搜索到 A 。
Tips:py2 经典类是按深度优先来继承的,新式类是按广度优先来继承的。
py3 经典类和新式类都是统一按广度优先来继承的。
4.1 Python3里的多继承顺序
先定义几个类,B 继承 A, C 继承 A, D 继承 B 和 C
class A(): def __init__(self): print("A") class B(A): def __init__(self): print("B") class C(A): def __init__(self): print("C") class D(B,C): def __init__(self): print("D")
当 D 里面没有参数时,到父类里找。我们知道,D 的父类是 B 和 C,且定义的顺序是 B在左,C在右。验证以下代码后,可以看到,继承顺序是从左到右的。
class A(): def __init__(self): print("A") class B(A): def __init__(self): print("B") class C(A): def __init__(self): print("C") class D(B,C): pass # def __init__(self): # print("D") D = D() # D里面没有,则找父类(从左往右),找到B # B
当 B 里面也没有参数时,D 就找父类 C
class A(): def __init__(self): print("A") class B(A): pass # def __init__(self): # print("B") class C(A): def __init__(self): print("C") class D(B,C): pass # def __init__(self): # print("D") D = D() # D里面没有,则找父类(从左往右),找到C # C
那么,当 B 和 C里都没有参数时,D该找谁呢?------答案是: A
class A(): def __init__(self): print("A") class B(A): pass # def __init__(self): # print("B") class C(A): pass # def __init__(self): # print("C") class D(B,C): pass # def __init__(self): # print("D") D = D() # A
四、面向对象之三大特性:封装,继承,多态
1.封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
第一步:将内容封装到某处
self 是一个形式参数,当执行 obj1 = Foo('wupeiqi', 18 ) 时,self 等于 obj1,当执行 obj2 = Foo('alex', 78 ) 时,self 等于 obj2
所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性,在内存里类似于下图来保存。
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过self间接调用
1、通过对象直接调用被封装的内容
上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
class Foo: def __init__(self, name, age): self.name = name self.age = age obj1 = Foo('wupeiqi', 18) print obj1.name # 直接调用obj1对象的name属性 print obj1.age # 直接调用obj1对象的age属性 obj2 = Foo('alex', 73) print obj2.name # 直接调用obj2对象的name属性 print obj2.age # 直接调用obj2对象的age属性
2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
class Foo: def __init__(self, name, age): self.name = name self.age = age def detail(self): print self.name print self.age obj1 = Foo('wupeiqi', 18) obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18 obj2 = Foo('alex', 73) obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
2.多态
多态,同一个对象,多种形态。python默认支持多态。
class F1: pass class S1(F1): def show(self): print('S1.show') class S2(F1): def show(self): print('S2.show') # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(obj): """Func函数需要接收一个F1类型或者F1子类的类型""" obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
3.类的约束
约束其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法,在python中约束的⽅式和⽅法有两种:
1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c#,所以使⽤频率还是很少的。
2. 使⽤⼈为抛出异常的⽅案.,并且尽量抛出的是NotImplementError,这样比较专业,⽽且错误比较明确.(推荐)。
class Payment: """ 此类什么都不做,就是制定一个标准,谁继承我,必须定义我里面的方法。 """ def pay(self,money): raise Exception("你没有实现pay方法") class QQpay(Payment): def pay(self,money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self,money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self,money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() c = Wechatpay() pay(a,100) pay(b,200) pay(c,300)
五、类的成员
1.细分类的组成成员
class A: company_name = '中国制造' # 静态变量(静态字段) __iphone = '1353333xxxx' # 私有静态变量(私有静态字段) def __init__(self,name,age): #特殊方法 self.name = name #对象属性(普通字段) self.__age = age # 私有对象属性(私有普通字段) def func1(self): # 普通方法 pass def __func(self): #私有方法 print(666) @classmethod # 类方法 def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print('类方法') @staticmethod #静态方法 def static_func(): """ 定义静态方法 ,无默认参数""" print('静态方法') @property # 属性 def prop(self): pass
2.类的私有成员
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员和公有成员的访问限制不同:
静态字段(静态属性)
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
普通字段(对象属性)
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
方法:
- 公有方法:对象可以访问;类内部可以访问;派生类中可以访问
- 私有方法:仅类内部可以访问;
总结:
对于这些私有成员来说,他们只能在类的内部使用,不能再类的外部以及派生类中使用。
3.类的其他成员
这里的其他成员主要就是类方法:
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
实例方法
定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
调用:只能由实例对象调用。
类方法
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:实例对象和类对象都可以调用。
静态方法
定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
调用:实例对象和类对象都可以调用。
双下方法(后面会讲到)
定义:双下方法是特殊方法,他是解释器提供的 由爽下划线加方法名加爽下划线 __方法名__的具有特殊意义的方法,双下方法主要是python源码程序员使用的,
我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。
调用:不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:__init__
4.字段
字段分为实例字段和静态字段,他们的区别主要是在内存中保存的位置不同,实例字段属于对象,静态字段属于类。
1 class Province: 2 country = "中国" # 静态字段(类变量,定义在类中,方法之外的变量) 3 4 def __init__(self, name): 5 self.name = name # 实例字段 6 7 8 province = Province("山东省") 9 print(province.name) 10 print(Province.country)
实例字段通过对象访问,静态字段通过类可以直接访问,也可以使用实例进行访问,如果是实例进行访问,首先寻找实例字段,没有则寻找类字段,如果还没有,则报错;如果使用实例.类变量修改类变量值后,我们通过实例.类变量实际上访问的是实例变量,类变量的值并没有修改。
class Student: classroom = "4班" address = "北京市" zhang = Student() li = Student() print(zhang.classroom) # 4班,访问的是类字段 print(li.classroom) # 4班,访问的是类字段 li.classroom = "6班" # 修改的是实例字段 print(zhang.classroom) # 4班,访问的是类字段 print(li.classroom) # 6班,访问的是实例字段 print(Student.classroom) # 4班 del li.classroom # 删除的是实例字段 print(li.classroom) # 4班访问的是类字段
5.属性
@property装饰器可以把类的方法伪装成属性调用,即obj.func()的调用方式变成obj.func的方式。
1 class Demo: 2 # 定义属性 3 @property 4 def f(self): 5 return "boxiaoyuan" 6 7 8 demo = Demo() 9 print(demo.f) # 调用属性
在定义属性时,只需要在普通方法的基础上添加@property即可,属性只有一个参数self。
Python的类有经典类(Python2)和新式类(Python3),新式类的属性有三种访问方式,即@property,@方法名.setter,@方法名.deleter,经典类只有一种,即在普通方法上增加@property。
1 # -*- coding:utf-8 -*- 2 3 4 class Person: 5 6 def __init__(self, name, age): 7 self.__name = name 8 self.__age = age 9 10 @property 11 def age(self): 12 return self.__age 13 14 @age.setter 15 def age(self, age): 16 if isinstance(age, int): 17 self.__age = age 18 else: 19 raise ValueError 20 21 @age.deleter 22 def age(self): 23 print("删除age数据") 24 25 26 if __name__ == '__main__': 27 person = Person("zhangsan", 18) 28 print(person.age) 29 person.age = 20 30 print(person.age) 31 del person.age
6.isinstace 与 issubclass
class A: pass class B(A): pass obj = B() print(isinstance(obj,B)) print(isinstance(obj,A))
isinstance(a,b):判断a是否是b类(或者b类的派生类)实例化的对象
class A: pass class B(A): pass class C(B): pass print(issubclass(B,A)) print(issubclass(C,A))
issubclass(a,b): 判断a类是否是b类(或者b的派生类)的派生类。