第六章 面向对象
6.1对象初始
- 面向的过程编程 vs 函数
s1 = "dasdasd"
count = 0
for i in s1:
count += 1
print(count)
l1 = [i for i in range(10)]
count = 0
for i in l1:
count += 1
print(count)
l1 = [i for i in range(10)]
def my_len(argv):
count = 0
for i in argv:
count += 1
print(count)
my_len(11)
# 减少了重复代码 增强了可读性
- 函数式编程 vs 面向对象编程
def login():
pass
def register():
pass
def shoping_car():
pass
def change_pwd():
pass
def check_paid_goods():
pass
def check_unpaid_goods():
pass
class Auth:
def login(self):
pass
def register(self):
pass
def change_pwd(self):
pass
class Shopping:
def shopping_car(self):
pass
def check_paid_goods(self):
pass
def check_unpaid_goods(self):
pass
# 面向对象第一个优点: 对相似功能的函数,同一个业务下的函数进行归类,分类.
"""
想要学习面向对象必须站在一个上帝的角度去分析考虑问题.
类: 具有相同属性和功能的一类事物.
对象:某个类的具体体现.
汽车: 汽车类, 楼下停着一个车牌号为9nb11的奥迪对象.
猫科类: 类. 陈硕家的养的那个大橘.对象.
鸡类: 一个类. 家里养的一只鸡.
男神:是一类. 太白对象.
面向对象的第二优点: 你要站在上帝的角度构建代码,类就是一个公共的模板,
对象就是从模板实例化出来的.
得到对象就得到了一切.
"""
6.1.1类的结构
class Human:
"""
类的具体结构
"""
# 第一部分:静态属性
mind = '有思想' # 类的属性 (静态属性, 静态字段)
language = '使用语言'
# 第二部分: 动态方法
def work(self):
print('人类都会工作')
def eat(self):
print('人类都需要吃饭')
6.1.2从类名的角度研究类
# class Human:
# """
# 类的具体结构
# """
# # 第一部分:静态属性
# mind = '有思想' # 类的属性 (静态属性, 静态字段)
# language = '使用语言'
#
# # 第二部分: 动态方法
# def work(self):
# print('人类都会工作')
#
# def eat(self):
# print('人类都需要吃饭')
# 1. 类名操作类中的属性
# 1. 类名查看类中所有的内容
# print(Human.__dict__)
# 2. 类名操作类中的静态属性 万能的点.
# 增:
# Human.body = '有头和四肢'
# 删:
# del Human.mind
# 改:
# Human.mind = 'liye脑残'
# 查:
# print(Human.language)
# print(Human.__dict__)
# 2. 类名调用类中的方法(一般类中的(静态方法,类方法)方法不会通过类名调用)
# Human.work(111)
# 总结:
# 一般类名就是操作类中的属性.
6.13从对象角度研究类
# class Human:
# """
# 类的具体结构
# """
# # 第一部分:静态属性
# mind = '有思想' # 类的属性 (静态属性, 静态字段)
# language = '使用语言'
#
# def __init__(self):
# # print(f'self---->: {self}')
# # print(666)
# self.name = '李业'
# self.age = 18
#
# # 第二部分: 动态方法
# def work(self):
# print('人类都会工作')
#
# def eat(self):
# print('人类都需要吃饭')
#
# obj = Human() # 实例化过程
# 得到一个返回值,这个返回值就是 对象,实例.
# print(f'obj---> {obj}')
# 实例化一个对象发生了三件事:
'''
1. 开辟一个对象空间.
2. 自动执行__init__方法,并且将对象地址传给self.
3. 运行__init__方法内的代码,给对象空间封装属性.
'''
class Human:
"""
类的具体结构
"""
# 第一部分:静态属性
mind = '有思想' # 类的属性 (静态属性, 静态字段)
language = '使用语言'
def __init__(self, name, age):
# print(f'self---->: {self}')
# print(666)
self.n = name
self.a = age
# 第二部分: 动态方法
def work(self):
# print(f'self---> {self}')
print(f'{self.n}都会工作')
def eat(self):
print(f'{self.n}都需要吃饭')
# obj = Human('李业',18) # 实例化过程
# print(obj.n)
# print(obj.a)
# print(obj.__dict__)
# 一:对象操作对象空间的属性
# 1. 对象查看对象的空间的所有属性
# obj = Human('李业',18)
# print(obj.__dict__)
# 2. 对象操作对象空间的属性
# obj = Human('李业',18)
# 增:
# obj.sex = 'laddy_boy'
# 删:
# del obj.a
# 改:
# obj.a = 1000
# 查:
# print(obj.n)
# print(obj.__dict__)
# 二 对象查看类中的属性
# obj = Human('李业',18)
# # print(obj.mind)
# obj.mind = '无脑的'
# print(obj.mind)
# print(Human.mind)
# 三 对象调用类中的方法
# obj = Human('孙戴维', 23)
# # print(f'obj---> {obj}')
# obj.work()
# obj.eat()
# 一个类可以实例化多个对象
# obj1 = Human('李业',18)
# obj2 = Human('小可爱', 16)
# obj3 = Human('怼姐', 18)
# 变量,函数名:
# age_of_oldboy = 73
# Ageofoldboy
6.2类的空间问题
6.2.1从空间角度研究类
- 何处可以添加对象属性?
- 在类的__init__可以添加, 在类的方法中也可以添加,在类的外部也可以添加
- 何处添加类的属性?
- 类的内部
- 类的外部
- 查询顺序:
- 对象.名字: 对象空间 _类对象指针 ------> 类空间 --->父类空间
- 类名,名字 ----> 父类空间
6.3类与类之间的关系
- 依赖关系
- 将一个类的类名或者对象传入另一个类的方法中.
class Elephant:
def __init__(self,name):
self.name = name
def open(self,obj):
print(f"{self.name}默念开门")
obj.beopen()
def close(self,obj):
print(f"{self.name}默念关门")
obj.beclose()
class Refrigerator:
def __init__(self,name):
self.name = name
def beopen(self):
print(f"{self.name}被打开了")
def beclose(self):
print(f"{self.name}被关闭了")
xiang = Elephant("琪琪")
haier = Refrigerator("海尔")
xiang.open(haier)
xiang.close(haier)
- 组合关系
- 给一个类的对象封装一个属性,次属性为另一个类的对象
class GameRole:
def __init__(self,name,hp):
self.name = name
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 = GameRole("盖伦",80)
zhaoxin = GameRole("赵信",70)
sword = Weapon("大保健",15)
musket = Weapon("红缨枪",30)
gailun.equipment(sword)
zhaoxin.equipment(musket)
gailun.weapon.weapon_attack(gailun,zhaoxin)
zhaoxin.weapon.weapon_attack(zhaoxin,gailun
6.4继承
6.4.1单继承
class Animal:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Human(Animal):
pass
class Dog(Animal):
pass
- Human Dog 子类 派生类
- Animal 父类 基类 超类
class Animal:
live = "有生命的"
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print("动物都需要进食")
class Human(Animal):
live = "活着"
类名执行父类实行方法
print(Huamn.live) Human.eat(111)
class Animal:
live = "有生命的"
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print("动物都需要进食")
class Human(Animal):
body = "有头有脸"
派生类对象 执行父类的属性方法
obj = Human("qq","nan",18) a1 = Animal('汪洋', '男', 48) print(a1.body) print(obj.live) obj.eat() print(obj)
查询顺序单向不可逆: 子类使用父类的属性方法,父类不能使用子类的属性方法.
既要执行子类的方法,又要执行父类的方法
- 方法一: 不依赖继承的
class Animal:
def __init__(self, name, sex, age):
self.name = name
self.age = age
self.sex = sex
class Human:
def __init__(self,name, sex, age, hobby):
'''
:param name: 李业
:param sex: 男
:param age: 18
:param hobby: 旅游
'''
# self = obj
# Animal.__init__(人类对象,姓名,性别,年龄)
Animal.__init__(self,name,sex,age)
self.hobby = hobby
class Dog(Animal):
pass
class Cat(Animal):
pass
obj = Human('驴友')
obj2 = Human('抽烟')
print(obj.__dict__)
obj = Human('李业','男',18,'旅游')
print(obj.__dict__)
def func(self):
self = 666
print(self)
self = 3
func(self)
func(666)
def func1(a,b):
print(a,b)
def func2(argv1,argv2,x):
func1(argv1,argv2)
print(x)
func2(1,2,666)
- 方法二: 依赖于继承 super()执行父类方法
class Animal:
def __init__(self, name, sex, age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('动物都需要吃饭')
class Human(Animal):
def __init__(self, name, sex, age, hobby):
# Animal.__init__(self,name,sex,age)
# super(Human,self).__init__(name, sex, age) #完整的写法
super().__init__(name,sex,age) # 执行父类的__init__方法,重构父类方法.
self.hobby = hobby
def eat(self):
print(f'{self.name}都需要吃饭')
# class Dog(Animal):
# pass
#
#
# class Cat(Animal):
# pass
#
obj = Human('李业','男',18,'旅游')
obj.eat()
# print(obj.__dict__)
6.4.2多继承
- python 类分
经典类: 不继承object类, 深度优先原则.
新式类:继承object类. mro(C3)算法
经典类的深度优先
- 从左找 找到最深
新式类的mro算法
mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C]) mro(B(D,E)) = [B] + merge(mro(D),mro(E),[D,E]) mro(B(D,E)) = [B] + merge([D,O],[E,O],[D,E]) mro(B(D,E)) = [B,D] + merge([O],[E,O],[E]) mro(B(D,E)) = [B,D,E] + merge([O],[O]) mro(B(D,E)) = [B,D,E,O] mro(C) = [C,E,F,O] mro(A(B,C)) = [A] + merge([B,D,E,O],[C,E,F,O],[B,C]) = [A,B] + merge([D,E,O],[C,E,F,O],[C]) = [A,B,D] + merge([E,O],[C,E,F,O],[C]) = [A,B,D,C] + merge([E,O],[E,F,O]) = [A,B,D,C,E] + merge([O],[F,O]) = [A,B,D,C,E,F,O]
6.5封装 多态 鸭子类型
class Student:
def __init__(self,name,sex):
self.name = name
self.sex = sex
liye = Student("李业","boy")
print(liye.sex)
6.5.1多态
python 一个变量可以指向多种数据
a = [1,2,3] a = "sdasd"
6.5.2 鸭子类型
class A:
def login(self):
print('登录')
def register(self):
print('注册')
def func1(self):
pass
def func2(self):
pass
class B:
def login(self):
print('登录')
def register(self):
print('注册')
def func3(self):
pass
A B互为鸭子.
虽然A,B两个类没有关系,但是我统一两个类中相似方法的方法名,在某种意义上统一的标准.
# index ,index, index, # str # list # a = [1,2,3] # a = list('123') # print(a)
6.6类的约束
版本1:
class QQpay:
def pay(self,money): # 鸭子类型
print(f'使用qq支付了{money}')
class Alipay:
def pay(self,money):
print(f'使用阿里支付了{money}')
obj1 = QQpay()
obj1.pay(100) # 支付接口
obj2 = Alipay()
obj2.pay(200)
版本2: 要做到统一接口
class QQpay:
def pay(self,money): # 鸭子类型
print(f'使用qq支付了{money}')
class Alipay:
def pay(self,money):
print(f'使用阿里支付了{money}')
def pay(obj,money): # 归一化设计 :统一接口
obj.pay(money)
obj1 = QQpay()
obj2 = Alipay()
pay(obj1,100)
pay(obj2,200)
版本3: 招了程序员,完善支付功能.
class QQpay:
def pay(self,money): # 鸭子类型
print(f'使用qq支付了{money}')
class Alipay:
def pay(self,money):
print(f'使用阿里支付了{money}')
class Wechat:
def fuqian(self,money):
print(f'使用微信支付了{money}')
def pay(obj,money): # 归一化设计 :统一接口
obj.pay(money)
obj1 = QQpay()
obj2 = Alipay()
pay(obj1,100)
pay(obj2,200)
obj3 = Wechat()
obj3.fuqian(300)
版本4: 制定约束.约定俗称,没有做到完全强制.
class Payment:
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(obj,money): # 归一化设计 :统一接口
obj.pay(money)
obj1 = QQpay()
obj2 = Alipay()
pay(obj1,100)
pay(obj2,200)
obj3 = Wechat()
obj3.fuqian(300)
#版本五 :做到强制约束.
#方法1: python语言惯于使用的一种约束方式,在父类主动抛出错误.
#方法2: 借鉴于Java语言,定义抽象类的概念,做到真正的强制约束.
方法一:
前提,你的项目已经上线了,之前完成的QQpay,Alipay 以及 pay函数这个接口都成型.
如果此时新添加一个微信支付,其他的py文件引用支付功能时还是直接引用pay.
class Payment:
def pay(self,money):
raise Exception('你的子类需要定义pay方法')
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()
pay(obj1,100)
pay(obj2,200)
obj3 = Wechat()
obj3.fuqian(300)
raise Exception('主动报错!')
print(111)
方法二:
from abc import ABCMeta,abstractmethod
抽象类,接口类: 强制指定规则(规范).
6.6super的深度解析
- 严格按照mro的执行顺序去执行
class A:
def f1(self):
print('in A f1')
def f2(self):
print('in A f2')
class Foo(A):
def f1(self):
super(Foo,self).f2() # 按照self对象从属于类的mro的顺序,执行Foo类的下一个类.
print('in A Foo')
obj = Foo()
obj.f1()
class A:
def f1(self):
print('in A')
class Foo(A):
def f1(self):
super(Foo,self).f1()
print('in Foo')
class Bar(A):
def f1(self):
print('in Bar')
class Info(Foo, Bar):
def f1(self):
super(Info,self).f1() # 按照self对象从属于类的mro的顺序,执行Info类的下一个类.
print('in Info f1')
print(Info.mro())
# [Info'>, Foo'>, Bar'>, A', 'object'>]
obj = Info()0
obj.f1()
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') # 1
class Info(Foo,Bar):
def f1(self):
super(Foo,self).f1()
print('in Info f1') # 2
# [Info Foo Bar A]
obj = Info()
obj.f1()
6.7类的私有成员
6.7.1私有类的属性
class A:
name = "李业"
__name = "钢哥"
def func(self):
print(self.name)
print(self.__name)
obj = A()
obj.func()
- 类的外部不能访问
class A:
name = "李业"
__name = "钢哥"
obj = A()
print(obj.name)
print(A.__name)
print(obj.__name)
- 类的派生类 不能访问
class A:
name = '李业'
__name = '钢哥'
class B(A):
def func(self):
print(self.__name)
obj = B()
print(obj.__name)
obj.func()
6.7.2私有对象属性
- 只能在类的内部使用,不能再类外部以及派生类使用.
class A:
def __init__(self,name,pwd):
self.name = name
self.__pwd = pwd
def dm5(self):
self.__pwd = self.__pwd + "123"
obj = A("李业","liyedsb")
print(obj.__pwd)
6.7.3私有类方法
class A:
def func(self):
self.__func()
print('in A func')
def __func(self):
print('in A __func')
obj = A()
obj.func()
obj.__func()
- 私有成员来说: 当你遇到重要的数据,功能,(只允许本类使用的一些方法,数据)设置成私有成员.
- python所有的私有成员都是纸老虎,形同虚设.
- 类从加载时,只要遇到类中的私有成员,都会在私有成员前面加上_类名 .
class A:
name = '李业'
__name = '钢哥' # 私有类的属性
def __func(self):
print('in __func')
print(A.__dict__)
print(A._A__name)
6.8类的其他方法
6.8.1类方法
类方法: 一般就是通过类名去调用的方法,并且自动将类名地址传给cls,
但是如果通过对象调用也可以,但是传的地址还是类名地址.
class A:
def func(self):
print('实例方法')
@classmethod
def cls_func(cls):
print(f'cls---->{cls}')
obj = cls()
print(obj)
print('类方法')
print(A)
A.cls_func()
obj = A()
obj.cls_func()
- 类方法有什么用?
- 得到类名可以实例化对象.
- 可以操作类的属性.
- 简单引用
- 创建学生类,只要实例化一个对象,写一个类方法,统计一下具体实例化多少个学生?
class Student:
count = 0
def __init__(self,name):
self.name = name
Student.addnum()
@classmethod
def addnum(cls):
cls.count += 1
@classmethod
def getnum(cls):
return cls.count
obj1 = Student('liye', 12343243243)
obj1 = Student('liye', 12343243243)
obj1 = Student('liye', 12343243243)
obj1 = Student('liye', 12343243243)
obj1 = Student('liye', 12343243243)
6.8.2静态属性
- 态方法是不依赖于对象与类的,其实静态方法就是函数.
- 保证代码的规范性,合理的划分.后续维护性高.
class A:
def func(self):
print("实例方法")
@classmethod
def cls_func(cls):
pass
@staticmethod
def static_func():
print("静态方法")
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
6.8.3属性
- 结果虽然实现了,但是逻辑上感觉不合理.bmi应该是类似于name,age,height,等名词,
- 但是你把它当做方法使用了.
lass BMI:
def __init__(self,height,weight):
self.height = height
self.weight = weight
@property
def get_bmi(self):
return self.weight / self.height ** 2
@get_bmi.setter
def get_bmi(self,values):
print("更改",values)
@get_bmi.deleter
def get_bmi(self):
print("删除")
zhy = BMI(1.83,65)
print(zhy.get_bmi)
zhy.get_bmi = 777
del zhy.get_bmi
- property 将执行一个函数需要函数名()变换成直接函数名.
- 将动态方法 伪装 成了一个属性,虽然在代码级别上没有什么提升,但是让你看起来
property 他是一个组合.
- 尝试更改伪装属性的值时 执行.setter装饰的函数
- 尝试删除伪装属性的值时 执行.deleter装饰的函数
应用场景:
- 工作中如果遇到了一些类似于属性的方法名,可以让其伪装成属性.
设置属性的两种方式:
- 利用装饰器设置属性.
class Foo:
@property
def bmi(self):
print('get的时候运行我啊')
@bmi.setter
def bmi(self,value):
print(value)
print('set的时候运行我啊')
# return 111 # 无法得到返回值
@bmi.deleter
def bmi(self):
print('delete的时候运行我啊')
# return 111 # 无法得到返回值
- 利用实例化对象的方式设置属性.
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
6.9 内置函数isinstance issubclass
- isinstaance(a,b) 判断的是 a是否是b类 或者 b类派生类 实例化的对象.
- iossubclass(a,b)判断的是 a类是否是b类 子孙类.
class A:
pass
class B(A):
pass
class C(B):
pass
print(issubclass(B,A))
print(issubclass(C,A))
obj1 = A()
obj2 = B()
print(isinstance(obj1,obj2))
6.10元类type
- python 中一切皆对象, 类在某种意义上也是一个对象,python中自己定义的类,
- 以及大部分内置类,都是由type元类(构建类)实例化得来的.
- type 与 object 的关系:
- object类是type类的一个实例.
- object类是type类的父类.
6.11反射
- 程序对自己内部代码的一种自省方式.
- 反射是什么? 通过字符串去操作对象的方式
- 可以反射的对象: 实例对象.类.本模块.其他模块.
class A:
def __init__(self):
self.lst = []
obj1 = A()
obj2 = A()
print(id(obj1.lst))
print(id(obj2.lst))
class A:
country = '中国'
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print('in A func')
obj = A('赵海狗',47)
hasattr
print(hasattr(obj,'name'))
print(hasattr(obj,'country'))
print(hasattr(obj,'func'))
print(getattr(obj,'name'))
print(getattr(obj,'func'))
f = getattr(obj,'func')
f()
print(getattr(obj,'sex',None))
if hasattr(obj,'name'):
getattr(obj,'name')
setattr,delattr 用的很少
obj.sex = '公'
print(obj.sex)
setattr(obj,'sex','公')
print(obj.__dict__)
delattr(obj,'name')
print(obj.__dict__)
- hasattr
- getattr
- setattr
- delattr
6.12函数与方法的区别
通过打印函数名的方式区别什么是方法,什么是函数. (了解)
通过类名调用的类中的实例方法叫做函数.
通过对象调用的类中的实例方法叫方法.
可以借助模块判断是方法还是函数.
rom types import FunctionType
from types import MethodType
def func():
pass
class A:
def func(self):
pass
obj = A()
总结: python 中一切皆对象, 类在某种意义上也是一个对象,python中自己定义的类,以及大部分内置类,都是由type元类(构建类)实例化得来的.python 中一切皆对象, 函数在某种意义上也是一个对象,函数这个对象是从FunctionType这个类实例化出来的.python 中一切皆对象, 方法在某种意义上也是一个对象,方法这个对象是从MethodType这个类实例化出来的.
总结: 如何判断类中的是方法还是函数.
- 函数都是显性传参,方法都是隐性传参.
6.13特殊的双下方法
- 特殊的双下方法: 原本是开发python这个语言的程序员用的.源码中使用的.
- str : 我们不能轻易使用.慎用.
- 双下方法: 你不知道你干了什么就触发某个双下方法.
定义:双下方法是特殊方法,他是解释器提供的 由爽下划线加方法名加双下划线 __方法名__的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。
调用:不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:init
6.13.1 __len__
class B:
def __len__(self):
print(666)
b = B()
len(b) # len 一个对象就会触发 __len__方法。
class A:
def __init__(self):
self.a = 1
self.b = 2
def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))
6.13.2 __hsah__
class A:
def __init__(self):
self.a = 1
self.b = 2
def __hash__(self):
return hash(str(self.a)+str(self.b))
a = A()
print(hash(a)
6.13.3 __str__
- 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class A:
def __init__(self):
pass
def __str__(self):
return '太白'
a = A()
print(a)
print('%s' % a)
6.13.4 __repr__
- 如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。
class A:
def __init__(self):
pass
def __repr__(self):
return '太白'
a = A()
print(repr(a))
print('%r'%a)
6.13.5 __call__
- 对象后面加括号,触发执行。
- 注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
6.13.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
a = A()
b = A()
print(a == b)
6.13.7 __del__
- 析构方法,当对象在内存中被释放时,自动触发执行。
- 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
6.13.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, *args, **kwargs)
a = A()
print(a.x)
- 单例模式
class A:
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance
- 单利模式具体分析
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
【采用单例模式动机、原因】
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
【单例模式优缺点】
【优点】
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
【缺点】
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用
单例模式具体分析
6.13.9__item__
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__)
6.13.10__iter__
class A:
def __init__(self,name):
self.name = name
def __iter__(self):
for i in range(10):
yield i
def __next__(self):
pass
obj = A('李业') # obj 一个可迭代对象
print('__iter__' in dir(obj))
for i in obj:
print(i)
print(obj.name)
o = iter(obj)
print(next(o))
print(next(o))
print(next(o))
print(next(o))
print(next(o))
print(next(o))
6.13.11__enter__ __exit__
# 如果想要对一个类的对象进行with as 的操作 不行。
class A:
def __init__(self, text):
self.text = text
with A('大爷') as f1:
print(f1.text)
class A:
def __init__(self, text):
self.text = text
def __enter__(self): # 开启上下文管理器对象时触发此方法
self.text = self.text + '您来啦'
return self # 将实例化的对象返回f1
def __exit__(self, exc_type, exc_val, exc_tb): # 执行完上下文管理器对象f1时触发此方法
self.text = self.text + '这就走啦'
with A('大爷') as f1:
print(f1.text)
print(f1.text)
class Diycontextor:
def __init__(self,name,mode):
self.name = name
self.mode = mode
def __enter__(self):
print "Hi enter here!!"
self.filehander = open(self.name,self.mode)
return self.filehander
def __exit__(self,*para):
print "Hi exit here"
self.filehander.close()
with Diycontextor('py_ana.py','r') as f:
for i in f:
print i
6.14异常处理
6.14.1错误分类
- 语法错误
- 逻辑错误
6.14.2结构
- 单分支
try:
num = int(input('>>>')) # 出现ValueError错误之后,直接跳转到except语句.
dic = {'name': '嘉欣'}
print(dic['age'])
print(111)
except ValueError:
print(666)
- 多分支
try:
num = int(input('>>>')) # 出现ValueError错误之后,直接跳转到except语句.
dic = {'name': '嘉欣'}
print(dic['age'])
l1 = [1, 2]
print(l1[100])
print(111)
except ValueError:
print('输入的有非数字元素')
except KeyError:
print('没有此键')
except IndexError:
print('没有此下标')
print(666)
- 万能异常: 处理所有python识别的异常.
try:
#
dic = {'name': '嘉欣'}
# print(dic['age'])
l1 = [1, 2]
print(l1[100])
print(111)
for i in 123:
pass
#
except Exception as e:
print(e)
print(666)
- 什么时候用万能? 什么时候用多分支?
- 如果你对错误信息不关心,只是想要排除错误让程序继续运行. 用万能异常.
- 你对错误信息要进行明确的分流,让你的程序多元化开发.
- 多分支+万能异常
def func():
pass
def func1():
pass
dic = {
1: func,
2: func1,
}
try:
num = int(input('请输入序号'))
dic[num]()
except ValueError:
print('请输入数字')
except KeyError:
print('请输入范围内的序号')
except Exception:
print('程序出现意料之外的错误....')
- try else finally
try:
dic = {'name': '嘉欣'}
# print(dic['age'])
l1 = [1, 2]
# print(l1[100])
print(111)
except KeyError:
print('没有此键')
except IndexError:
print('没有此下标')
else:
print('如果没有出现异常则执行这里')
finally:
print('finally 666')
except 必须依赖于try, else必须依赖于except和try
- finally只是依赖于try.
- finally : 在异常出现之前,执行finally语句.
- finally 用在 关闭数据库连接,文件句柄关闭,数据保存等,用到finally.
在return结束函数之前,执行finally代码.
- 主动触发异常.
raise ValueError('出现了value错误')
- 断言: 展现出一种强硬的态度.
assert 条件
name = 'alex'
n1 = input('请输入:')
assert name == n1
print(111)
print(222)
- 自定义异常
- python中给你提供的错误类型很多,但是不是全部的错误.
class LiYeError(BaseException):
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
try:
raise LiYeError('socket.connent.....')
except LiYeError as e: # e = LiYeError('类型错误')
print(e)