python之面向对象
第一章 面向对象初识
面向对象的三大特性是什么? 抽象、继承、多态。
面向对象第一个优点:*
对相似功能的函数,同一个业务的函数进行归类,分类,使你的代码更清晰化,更合理化。
什么是面向对象。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
那什么是类?什么是对象?
类:就是具有相同属性和功能的一类事物。
对象:就是类的具体表现。对象一定是不一样的
面向对象的第二个优点:
面向对象,要拥有上帝的视角看问题,类其实就是一个公共模板(厂房),对象就是从具体的模板实例化出来,得对象,得一切
1. 面向对象的结构
class Human:
"""
类的具体结构
"""
#第一部分:静态属性
mind = "思想"
language ="语言"
#第二部分:动态方法
def work(self):
print("工作")
def eat(self):
print("吃饭")
class 是关键字与def用法相同,定义一个类。
Human是此类的类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。
类的结构从大方向来说就分为两部分:
静态变量。
动态方法。
1. 从类名的角度研究类
1.类名操作类中的属性
class Human:
"""
类的具体结构
"""
#第一部分:静态属性
mind = "思想"
language ="语言"
#第二部分:动态方法
def work(self):
print("工作")
def eat(self):
print("吃饭")
1.类名查看类中所有的内容
print(Human.__dict__)
2.类名操作类中的静态属性
1.增加
Human.body="有头发"
print(Human.__dict__)
2.删除
del Human.mind
3.改
Human.mind ="个性"
print(Human.__dict__)
4.查
print(Human.language)
# print(Human.__dict__)
2.类名操作动态方法
除了两个特殊方法:静态方法,类方法之外,一般不会通过类名操作一个类中的方法.
Human.work(123)
总结:一般类名
3. 从对象的角度研究类
1.什么是对象
对象是从类中出来的,只要是类名加上(),这就是一个实例化过程,这个就会实例化一个对象。**
实例化一个对象总共发生了三件事:
1,在内存中开辟了一个对象空间。
2,自动执行类中的____init____方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
3,在____init__ _方法中通过self给对象空间添加属性。
class Human:
"""
类的具体结构
"""
mind = "思想"
language ="语言"
def __init__(self,name,age):
# self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。
self.n=name
self.a=age
def work(self):
print("工作")
def eat(self):
print("吃饭")
obj=Human("zbb",18) #实例化过程
#得到一个返回值,这个返回值就是对象,实例
print(obj.n)
print(obj.a)
print(obj.__dict__)
#zbb
#{'n': 'zbb'}
2.对象操作对象空间属性
1.对象查看对象的空间的所有属性
obj = Human('zbb',18)
print(obj.__dict__)
2. 对象操作对象空间的属性
obj = Human('zbb',18)
增:
obj.sex = 'laddy_boy'
删:
del obj.a
改:
obj.a = 1000
查:
print(obj.n)
print(obj.__dict__)
3.对象查看类中的属性
obj = Human('zbb',18)
# print(obj.mind)
obj.mind = '无脑的'
print(obj.mind)
print(Human.mind)
4.对象调用类中的方法
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
self.n = name
self.s = sex
self.a = age
self.h = hobby
def work(self):
print(self)
print('人类会工作')
def tools(self):
print('人类会使用工具')
obj = Human('barry','男',18,'运动')
obj.work()
obj.tools()
5.self 是什么?
self其实就是类中方法(函数)的第一个位置参数,只不过解释器会自动将调用这个函数的对象传给self。所以咱们把类中的方法的第一个参数约定俗成设置成self, 代表这个就是对象。
一个类可以实例化多个对象
obj1= Human('小胖','男',20,'美女')
obj2= Human('相爷','男',18,'肥女')
print(obj1,obj2)
print(obj1.__dict__)
print(obj2.__dict__)
第二章 类空间问题以及类之间的关系
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 方法也可以。
2.何处可以添加类的静态属性
class A:
def __init__(self, name):
self.name = name
def func(self, sex):
self.sex = sex
def func1(self):
A.bbb = 'ccc'
# 类的外部可以添加
A.aaa = 'djb'
print(A.__dict__)
# 类的内部也可以添加。
A.func1(111)
print(A.__dict__)
3. 对象如何找到类的属性
对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->.....
类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> ........
上面的顺序都是单向不可逆,类名不可能找到对象的属性。
4.类与类之间的关系
- 依赖关系
- 关联关系
- 组合关系
- 聚合关系
- 实现关系
- 继承关系(类的三大特性之一:继承。)
1.依赖关系
主从之分
依赖关系:将一个类的对象或者类名传到另一个类的方法使用。
2.关联,组合,聚合关系
- 关联关系. 两种事物必须是互相关联的. 但是在某些特殊情况下是可以更改和更换的.
- 聚合关系. 属于关联关系中的⼀种特例. 侧重点是xxx和xxx聚合成xxx. 各⾃有各⾃的声明周期. 比如电脑. 电脑⾥有CPU, 硬盘, 内存等等. 电脑挂了. CPU还是好的. 还是完整的个体
- 组合关系. 给对象封装一个属性,属性值是另一个类的对象
class Boy:
def __init__(self,name,girl=None):
self.name = name
self.girl=girl
def hava_diner(self):
if self.girl:
print(f"{self.name}请{self.girl}吃饭")
else:
print("单身狗")
live=Boy("qw")
# live.hava_diner()
live.girl="zxy"
live.hava_diner()
class Boy:
def __init__(self,name,girl=None):
self.name = name
self.girl=girl
def hava_diner(self):
if self.girl:
print(f"{self.name}请{self.girl}吃饭")
else:
print("单身狗")
def girl_skill(self):
self.girl.skill()
class Girl:
def __init__(self,name):
self.name=name
def skill(self):
print(f"{self.name}能吃")
zbb=Boy("zbb")
# live.hava_diner()
zxy =Girl("zxy")
zbb.girl=zxy
zbb.girl_skill()
class Game_role:
def __init__(self,name,ad,hp):
self.name=name
self.ad=ad
self.hp=hp
def equipment(self,wea):
self.weapon =wea #组合关系
class Weapon:
def __init__(self,name,ad):
self.name = name
self.ad = ad
def weapon_attack(self,p1,p2): #依赖关系
p2.hp-=self.ad
print(f"{p1.name}利用{self.name}给了{p2.name}一枪,{p2.name}掉了{self.ad}血,还剩{p2.hp}")
gailun =Game_role("盖伦",10,100)
yasuo =Game_role("剑豪",20,80)
Sword = Weapon("大保健",16)
Musket = Weapon('菊花枪',20)
# 给游戏人物封装武器属性
gailun.equipment(Sword)
gailun.weapon.weapon_attack(gailun,yasuo)
第三章 继承
1.什么是面向对象的继承
比较官方的说法就是:
继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充
专业说法: 如果B类继承A类
B类就称为子类,派生类
A类称为父类,基类,超类
通俗一点就是 子承父业
# 继承的用法:
class Aniaml:
live = "123"
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
优点: 减少重复代码 .
增加类之间的耦合度(耦合性不宜多,宜精)
2.继承的分类
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object
3.单继承
1.类名,对象执行父类的方法
class Animal:
live = "有生命"
def __init__(self,name,sex,age):
self.name =name
self.sex =sex
self.age =age
def eat(self):
print("chi")
class Human(Animal):
body = "有思想"
#类名执行父类属性的方法(不常用)
print(Human.live)
Human.eat(1213)
#子类执行父类的方法
obj = Human("zbb","男","23") #相当于实例化
print(obj.live)
2.执行顺序
p1 = Person('barry','男',18)
# 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
p1.eat()
# 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
3.同时执行子类以及父类方法
第一种方法(不常用)
class Animal:
def eat(self):
print("chi")
class Human:
def __init__(self,name):
self.name =name
def eat(self):
print(f"{self.name}需要吃饭")
Animal.eat(self.name)
obj = Human("zbb")
obj.eat()
第二种方法
class Animal:
def eat(self):
print("chi")
class Human(Animal):
def __init__(self,name):
self.name =name
def eat(self):
print(f"{self.name}需要吃饭")
super().eat()
obj = Human("zbb")
obj.eat()
super() 重构父类的方法
解决多重继承问题
不需要明确给出任何基类的名字,它会自动找到所有直接基类,及其对应的方法.用于继承.
super()的好处就是可以避免直接使用父类的名字.主要用于多重继承
4.多继承
在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object
1.经典类的多继承
经典类: 深度优先.从左至右,深度优先.
不继承object类
2.新式类的多继承
继承object类
class O:
name = '1'
class D(O):
pass
class E(O):
name = '2'
# pass
class F(O):
name = '3'
class B(D,E):
pass
class C(E,F):
pass
class A(B,C):
pass
obj = A()
print(obj.name)
# mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
# mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C])
总结:直接print 不需要什么算法
print(A.mro())
mro是继承付父类方法时的顺序表
第四章 封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
第一步:将内容封装到某处
self 是一个形式参数,当执行 obj1 = Foo( ) 时,self 等于 obj1
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过self间接调用
1、通过对象直接调用被封装的内容
上图展示了对象 ojb1在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
obj1 = Foo("zbb",18)
print(obj1.name) # 直接调用obj1对象的name属性
print(obj1.age) # 直接调用obj1对象的age属性
2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print(self.name)
print(self.age)
obj1 = Foo("zbb",18)
obj1.func() ## Python默认会将obj1传给self参数
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
第五章 多态
多态,同一个对象,多种形态。python默认支持多态。
鸭子类型
python中有一句谚语说的好,你看起来像鸭子,那么你就是鸭子。
对于代码上的解释其实很简答:
class A:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
class B:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
obj = A()
obj.f1()
obj.f2()
obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。
# 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。
# 这样的例子比比皆是:str tuple list 都有 index方法,这就是统一了规范。
# str bytes 等等 这就是互称为鸭子类型。
鸭子类型
第六章 类的约束
class Payment:
def pay(self,money):
raise Exception("你的子类需要定义pay方法")
#pass
class QQpay(Payment):
def pay(self,money):
print(f"使用qq支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用阿里支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用微信支付了{money}")
def pay(obj,money): #统一接口
obj.pay(money) # 这个函数就是统一支付规则,这个叫做: 归一化设计。
obj1 = QQpay()
obj2 = Alipay()
obj3 = Wechat()
pay(obj1,100)
pay(obj2,200)
# pay(obj3,200) # 会直接找到父类 并发出错误
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
#抽象类 接口类 规范和约束 metaclass指定的是一个元类
@abstractmethod
def pay(self,money):
pass #抽象方法
class QQpay(Payment):
def pay(self,money):
print(f"使用qq支付了{money}")
class Alipay(Payment):
def pay(self,money):
print(f"使用阿里支付了{money}")
class Wechat(Payment):
def fuqian(self,money):
print(f"使用微信支付了{money}")
# def pay(self,money):#强制定义 不定义会报错
# pass
def pay(obj,money): #统一接口
obj.pay(money)
obj3 = Wechat() #实例化过程 就报错了
总结: 约束. 其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法. 在python中约束的⽅式和⽅法有两种:
1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c#. 所以使⽤频率还是很少的
2.使⽤⼈为抛出异常的⽅案. 并且尽量抛出的是NotImplementError. 这样比较专业, ⽽且错误比较明确.(推荐)
第七章 super()深入了解
super是严格按照类的继承顺序执行!!!
# super可以下一个类的其他方法
# 严格按照类的mro顺序执行
class A:
def f1(self):
print('in A')
class Foo(A):
def f1(self):
super().f1()
print('in Foo')
class Bar(A):
def f1(self):
print('in Bar')
class Info(Foo,Bar):
def f1(self):
super(Foo,self).f1()
#super(Info,self).f1() #()默认是
print('in Info f1')
obj = Info()
obj.f1()
第八章 带颜色的print
书写格式: 开头部分:\033[显示方式;前景色;背景色m + 结尾部分:\033[0m
注意:开头部分的三个参数:显示方式,前景色,背景色是可选参数,可以只写其中的某一个;另外由于表示三个参数不同含义的数值都是唯一的没有重复的,所以三个参数的书写先后顺序没有固定要求,系统都能识别;但是,建议按照默认的格式规范书写。
对于结尾部分,其实也可以省略,但是为了书写规范,建议\033[***开头,\033[0m结尾。
-------------------------------------------
-------------------------------------------
字体色 | 背景色 | 颜色描述
-------------------------------------------
30 | 40 | 黑色
31 | 41 | 红色
32 | 42 | 绿色
33 | 43 | 黃色
34 | 44 | 蓝色
35 | 45 | 紫红色
36 | 46 | 青蓝色
37 | 47 | 白色
-------------------------------------------
-------------------------------
显示方式 | 效果
-------------------------------
0 | 终端默认设置
1 | 高亮显示
4 | 使用下划线
5 | 闪烁
7 | 反白显示
8 | 不可见
-------------------------------
数值表示的参数含义:
显示方式: 0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、 5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
前景色: 30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋 红)、36(青色)、37(白色)
背景色: 40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋 红)、46(青色)、47(白色)
常见开头格式:
\033[0m 默认字体正常显示,不高亮
\033[32;0m 红色字体正常显示
\033[1;32;40m 显示方式: 高亮 字体前景色:绿色 背景色:黑色
\033[0;31;46m 显示方式: 正常 字体前景色:红色 背景色:青色
举例说明:
1,
print('\033[1;35;0m字体变色,但无背景色 \033[0m') # 有高亮 或者 print('\033[1;35m字体有色,但无背景色 \033[0m')
print('\033[1;45m 字体不变色,有背景色 \033[0m') # 有高亮
print('\033[1;35;46m 字体有色,且有背景色 \033[0m') # 有高亮
print('\033[0;35;46m 字体有色,且有背景色 \033[0m') # 无高亮
第九章 类的成员
1.细分类的组成成员
class A:
company_name = 'zbb' # 静态变量(静态字段)
__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.类的私有成员
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员和公有成员的访问限制不同:
1.静态字段(静态属性)
公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
私有静态字段:仅类内部可以访问;
class A:
name = 'zbb'
__name = "zmn"
def func(self):
print(self.name)
print(self.__name)
obj = A()
obj.func() #内部可以访问
外部不能访问
class A:
name = 'zbb'
__name = "zmn"
def func(self):
pass
obj = A()
print(obj.name)
print(obj.__name) #报错
print(A.__name) #报错
2.对象属性
公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
私有普通字段:仅类内部可以访问;
class A:
def __init__(self,name,pwd):
self.name = name
self.__pwd = pwd
def md5(self):
self.__pwd = self.__pwd + "123"
obj = A('zbb',"123")
print(obj.__pwd) ###报错 不能在类外部以及派生类使用
3.方法:
公有方法:对象可以访问;类内部可以访问;派生类中可以访问
私有方法:仅类内部可以访问;
class A:
def func(self):
self.__func()
print("1")
def __func(self):
print("2")
obj = A()
obj.func() #内部可以调用
# obj.__func() 报错
#####print(A._A__func) #(不允许!!!)
总结:
私有成员来说: 当你遇到重要的数据,功能,(只允许本类使用的一些方法,数据)设置成私有成员.
ps:非要访问私有成员的话,可以通过 对象._类__属性名,但是绝对不允许!!!*
为什么可以通过._类__私有成员名访问呢?因为类在创建时,如果遇到了私有成员(包括私有静态字段,私有普通字段,私有方法)它会将其保存在内存时自动在前面加上_类名.
3.类的其他方法
实例方法
定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
调用:只能由实例对象调用。
1.类方法
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:实例对象和类对象都可以调用。
类方法有什么用???
得到类名可以实例化对象.
可以操作类的属性.
class A:
def func(self):
print("实例方法")
@classmethod
def cls_func(cls):
print(f"cls{cls}") #cls类名
obj = cls()
print(obj) #打印A的内存地址
print("类方法")
A.cls_func()
#obj = A()
#obj.cls_func()
# 类方法: 一般就是通过类名去调用的方法,并且自动将类名地址传给cls,
# 但是如果通过对象调用也可以,但是传的地址还是类名地址.
例题
创建学生类,只要实例化一个对象,写一个类方法,统计一下具体实例化多少个学生?
class Student:
count = 0
def __init__(self,name,id):
self.name = name
self.id = id
Student.addnum()
@classmethod
def addnum(cls):
cls.count = cls.count + 1
@classmethod
def getnum(cls):
return cls.count
obj1 = Student('zbb', 12343243243) #
obj1 = Student('zbb', 12343243243) #
print(Student.getnum())
2.静态方法
使用装饰器@staticmethod。
调用:实例对象和类对象都可以调用。
# # 静态方法是不依赖于对象与类的,其实静态方法就是函数.
# 保证代码的规范性,合理的划分.后续维护性高.
#和在外面没有区别
import time
class TimeTest(object):
area = '中国'
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def change_time(self):
print(f'你想调整的时间: {self.hour}时{self.minute}分{self.second}秒')
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
def time1():
pass
def time2():
pass
# t = TimeTest(2, 10, 10)
# # t.change_time()
# print(TimeTest.showTime())
3.属性
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
将动态方法 伪装 成了一个属性,虽然在代码级别上没有什么提升,但是让你看起来更合理
例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
class Bmi:
def __init__(self,name,height,weight):
self.name = name
self.height = height
self.weight = weight
def bmi(self):
return self.weight/self.height**2
obj = Bmi('赵嘎', 1.83, 65)
print(obj.bmi())
结果虽然实现了,但是逻辑上感觉不合理.bmi应该是类似于name,age,height,等名词,
但是你把它当做方法使用了.
class Bmi:
def __init__(self,name,height,weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight/self.height**2
obj = Bmi('赵嘎', 1.83, 65)
# print(obj.bmi)
# property 将执行一个函数需要函数名()变换成直接函数名.
# 将动态方法 伪装 成了一个属性,虽然在代码级别上没有什么提升,但是让你看起来更合理.
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')
@AAA.setter
def AAA(self,value):
print('set的时候运行我啊')
@AAA.deleter
def AAA(self):
print('delete的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
# get的时候运行我啊
# set的时候运行我啊
# delete的时候运行我啊
class Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,value):
print('set的时候运行我啊')
def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
使用场景
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deltter
def price(self, value):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
商品实例
商品示例
第十章 isinstace 与 issubclass
isinstance(a,b):判断a是否是b类(或者b类的派生类)实例化的对象
class A:
pass
class B(A):
pass
obj = B()
print(isinstance(obj,B))
print(isinstance(obj,A))
isinstance
isinstace(a,b) 判断的是 a类是否是b类 子孙类.**
issubclass(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
思考:那么 list str tuple dict等这些类与 Iterble类 的关系是什么?
from collections import Iterable
print(isinstance([1,2,3], list)) # True
print(isinstance([1,2,3], Iterable)) # True
print(issubclass(list,Iterable)) # True
# 由上面的例子可得,这些可迭代的数据类型,list str tuple dict等 都是 Iterable的子类。
第十章 type与object联系
type元类是获取该对象从属于的类,而type类比较特殊,Python原则是:一切皆对象,其实类也可以理解为'对象',而type元类又称作构建类,
python中大多数内置的类(包括object)以及自己定义的类,都是由type元类创造的。
print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': 'zbb'}))
print(type((1,2,3)))
print(type(object))
class A:
pass
print(isinstance(object,type))
print(isinstance(A, type))
type 与 object 的关系.
print(type(object)) object类是type类的一个实例.
object类是type类的父类.
print(issubclass(type,object))
第十一章 反射
程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
python面向对象中的反射:
通过字符串的形式操作对象相关的属性。
python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
对对象的反射
class Foo:
f="类的静态变量"
def __init__(self,name,age):
self.name = name
self.age = age
def say_hi(self):
print(f"hello {self.name}")
#实例化对象
obj = Foo("zbb",23)
#检测是否含有某属性
print(hasattr(obj,"name"))
print(hasattr(obj,"say_hi"))
# 获得属性(可以获得是字符串)
n = getattr(obj,"name")
# print(obj.name)
print(n)
func=getattr(obj,'say_hi')
# obj.say_hi()
func()
# print(getattr(obj,'aaaaaaaa')) #报错
print(getattr(obj,'aaaaaaaa','不存在啊')) #报错内容自己定义
# 设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'nb')
print(obj.__dict__)
print(obj.show_name(obj))
#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,则报错
print(obj.__dict__)
对类的反射
class A:
country = "中国"
def __init__(self,name,age):
self.name = name
self.age =age
def func(self):
print(self)
print("in A")
if hasattr(A,"country"):
print(getattr(A,"country"))
if hasattr(A,"func"):
obj = A("zbb","18")
getattr(obj,"func")()
当前模块的反射
import sys
def s1():
print('s1')
def s2():
print('s2')
this_module = sys.modules[__name__] #本模块这个对象
hasattr(this_module, 's1')
getattr(this_module, 's2')
在当前模块中一次性执行4个函数
def func1():
print('in func1')
def func2():
print('in func2')
def func3():
print('in func3')
def func4():
print('in func4')
import sys
func_lst = [f'func{i}' for i in range(1,5)]
# print(func_lst)
for func in func_lst:
getattr(sys.modules[__name__],func)()
对其他模块的反射
#一个模块中的代码
def test():
print('from the test')
# 另一个模块中的代码
import module_test as obj
#obj.test()
print(hasattr(obj,'test'))
getattr(obj,'test')()
反射的应用:
了解了反射的四个函数。那么反射到底有什么用呢?它的应用场景是什么呢?
现在让我们打开浏览器,访问一个网站,你单击登录就跳转到登录界面,你单击注册就跳转到注册界面,等等,其实你单击的其实是一个个的链接,每一个链接都会有一个函数或者方法来处理
class User:
user_list = [('login','登录'),('register','注册'),('save', '存储')]
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
while 1:
choose = input('请输入序号: \n1: 登录\n2: 注册\n3: 存储\n').strip() # 1
obj = User()
getattr(obj, obj.user_list[int(choose)-1][0])() # getattr(obj,'login')
第十二章 函数 vs 方法
1.通过打印函数(方法)名确定(了解)
def func():
pass
class A:
def func(selfs):
pass
print(func)
print(A.func)#通过类名调用的类中的实例方法叫函数
<function func at 0x00000252B19DC2F0>
<function A.func at 0x00000252B1B997B8>
obj= A()
print(obj.func)#通过对象调用的类中的实例方法叫方法.
2 通过types模块验证
from types import FunctionType
from types import MethodType
def func():
pass
class A:
def func(self):
pass
obj = A()
print(isinstance(func,FunctionType)) # True
print(isinstance(A.func,FunctionType)) # True
print(isinstance(obj.func,FunctionType)) # False
print(isinstance(obj.func,MethodType)) # True
总结:
python 中一切皆对象, 类在某种意义上也是一个对象,python中自己定义的类,
以及大部分内置类,都是由type元类(构建类)实例化得来的.
python 中一切皆对象, 函数在某种意义上也是一个对象,函数这个对象是从FunctionType这个类实例化出来的.
python 中一切皆对象, 方法在某种意义上也是一个对象,方法这个对象是从MethodType这个类实例化出来的.
3 静态方法是函数
4 函数与方法的区别
那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。
(1)函数的是显式传参的。
(2)函数则跟对象无关。
(3)方法中的数据则是隐形传参.
(4)方法可以操作类内部的数据。
(5)方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中。
第十三章 双下方法
定义:双下方法是特殊方法
双下方法主要是python源码程序员使用的,
我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。
调用:不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:_init
1. len
class B:
def __init__(self,name,age):
self.name = name
def __len__(self):
print(self.__dict__)
return len(self.__dict__) #必须要加
b = B("zbb",28)
print(len(b)) #len()触发调用
2.hash
class A:
pass
obj = A()
print(hash(obj))
print(hash('fdsaf')) # hash()触发调用
3.str重点
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class A:
def __init__(self,name,age):
self.name = name
self.age =age
def __str__(self):
print(666)
return f'姓名: {self.name} 年龄: {self.age}'
a = A("zbb",18)
print(f'{a.name},{a.age}') #打印对象名触发__str__方法
print(str(a)) #直接str转化也可以触发.
4.repr
如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。
print('我叫%s' % ('zbb'))
print('我叫%r' % ('zbb')) #保留括号里的原来的样子
print(repr('fdsaf'))
# 我叫zbb
# 我叫'zbb'
# 'fdsaf'
class A:
def __init__(self,name,age):
self.name = name
self.age =age
def __repr__(self):
print(666)
return f'姓名: {self.name} 年龄: {self.age}'
a = A('zbb',18)
# print(a)
print(repr(a)) #print(repr(对象名)) 触发
5.call重点
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo()
obj() # 对象名() 或者 类名()() 触发
# Foo()()
6.eq
class A:
def __init__(self):
self.a = 1
self.b = 2
def __eq__(self,obj):
# if self.a == obj.a and self.b == obj.b:
# return True
return True
x = A()
y = A()
print(x == y) # 对象名 == 对象名 会触发
节省内存
7.del
class A:
def __del__(self):
print(666)
obj = A()
del obj
# del 变量名 或者 类 或者 方法名时会执行
#不需要去程序员去关心,垃圾处理机制帮助处理
8.new 重点
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(A) # object 342534
a = A()
# 1. 先触发 object的__new__方法,此方法在内存中开辟一个对象空间.
# 2. 执行__init__方法,给对象封装属性.
# python中的设计模式: 单例模式
# 一个类只允许实例化一个对象.
class A:
pass
obj = A()
print(obj)
obj1 = A()
print(obj1)
obj2 = A()
print(obj2) #地址都不同
手写单例模式
方便对实例个数的控制并节约系统资源
class A:
__msg = None # 用来记录对象信息
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
if not cls.__msg: # 当 __msg 为 None 时,证明是第一次创建对象空间
# cls.__msg = super().__new__(cls) # 调用父类的__new__方法。 记得参数为 cls(类本身)
cls.__msg = object.__new__(cls)
return cls.__msg # 将对象空间返回
a = A("zbb")
a1 = A("zxy")
print(a.name)
print(a1.name) # 两个地址相同
9.item
# __item__系列
# __getitem__ __setitem__ __delitem__ 对对象做类似于字典的(增删改查)触发__item__系列
# __delattr__ del obj.属性 就会触发此方法
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
10.enter exit
上下文管理
# 实例化对象的第二种方式: 必须基于 __enter__ 以及 __exit__这个两个方法.
class A:
def __init__(self, text):
self.text = text
def __enter__(self): # 开启上下文管理器对象时触发此方法
self.text = self.text + '您来啦' # 第一步
print(11111)
return self # 必须!!!将实例化的对象返回f1
def __exit__(self, exc_type, exc_val, exc_tb): # 执行完上下文管理器对象f1时触发此方法
print(333) # 第三步
self.text = self.text + ',这就走啦'
with A('大爷') as f1:
print(2222)
print(f1.text) # 第二步
print(f1.text) # 第四步
11.iter
class A:
def __init__(self,name):
self.name = name
def __iter__(self):
for i in range(10):
yield i
obj = A('zbb') # obj 一个可迭代对象
# print('__iter__' in dir(obj))
for i in obj:
print(i)
class A:
def __init__(self,name):
self.name = name
def __iter__(self):
for i in range(10):
yield i
obj = A('zbb') # obj 一个可迭代对象
o = iter(obj)
print(next(o))
print(next(o))
print(next(o))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?