面向对象:类 class 和 对象 object:
01. 什么是对象: 对象是指现实中的物体或实例02. 什么是面向对象:
把一切看成对象(实例),对象和对象之间用方法(行为)建立关联关系 面向过程是一件事怎么去一步一步实现, 面向对象是一件事有谁(实例)去实现03. 什么是类:
拥有相同属性和行为的对象分为一组, 即为一个类 类是用来描述对象的工具04. 面向对象示意:
车(类) ----->> BYD E6(京A.88888) 对象(实例) 狗(类) ----->> 小京巴(户籍号: 00001) 对象(实例) int(类) ----->> 100(对象,实例)05. 类的创建语法:
class 类名(继承列表): """类文档字符串""" 实例方法(类内的函数methd) 的定义 类变量(class variable) 定义 类方法(@classmethod)定义 静态方法(@staticmethod)定义06. 类的作用:
可以用类创建一个或多个此类的对象(实例) 类内的变量和方法能被此类所创建的所有实例所共同拥有 说明: 类名必须是标识符(与变量名命名规则相同, 建议首字母大写) 类名实质上就是变量, 它绑定一个类实例 类的定义最后面要加两个空格以告诉解释执行器, 类的定义已经结束 示例: class Dog: pass 术语: 类 对象 实例 class object instance07. 构造函数:
构造函数的调用表达式: 类名([创建传参列表]) [] 里的内容代表可省略 作用: 创建这个类的实例对象, 并返回此实例对象的引用关系 示例: class Dog: # 定义一个狗类 pass dog1 = Dog() # 用类来创建一个对象用dog1绑定 print(id(dog1)) # 打印dog1所在的内存地址 dog2 = Dog() # 创建第二个对象 用dog2绑定 print(id(dog2)) print(dog1 is dog2) # 判断两只狗是不是同一条狗, False 实例说明: 实例有自己的作用域或名字空间, 可以为该实例添加实例变量(也叫属性) 实例可以调用类的方法 实例可以访问类中的类变量08. 实例变量(属性 attribute):
每个实例可以有自己的变量, 称为实例变量(属性) 属性的使用语法: 实例.属性名 01. 属性的赋值规则: (同变量规则相同) 01. 首次为属性赋值则创建此属性 02. 再次为属性赋值则改变属性的绑定关系 示例: class Dog: pass dog1 = Dog() dog1.kinds = "京巴" #为dog1绑定的实例添加kinds属性 dog1.color = "白色" # 添加属性 print(dog1.kinds, dog1.color) # 访问属性 dog1.color = '黄色' # 修改dog1.color 的绑定关系 print(dog1.color)02. 删除属性:
del 语句 语法: del 对象.属性名 示例: class Student: pass stu = Student() stu.name = 'xiaozhang' # 创建属性 print(stu.name) del stu.name # 删除此属性 print(stu.name) # 属性错误,因为属性已经不存在了 03. 查找顺序: 对象.属性 : 先从对象空间找,如果找不到,再从类空间找,再找不到,再从父类找.... 类名.属性 : 先从本类空间找,如果找不到,再从父类找....09. 实例方法 method:
语法: class 类名(继承列表): def 实例方法名(self, 参数1, 参数2, ....): 语句块 作用: 用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为 说明: 实例方法的实质是函数, 是定义在类内的函数 实例方法的第一个参数代表调用这个方法的实例, 一般命名为'self' 实例方法的调用语法: 实例.实例方法名(调用参数) 或 类名.实例方法名(实例, 调用参数) 初始化方法: 作用: 对新创建的对象添加属性等必须的资源 语法形式: class 类名: # [] 代表其中内容可省略 def __init__(self[, 参数列表]): 语句块 说明: 初始化方法名必须为 __init__ 不可改变 初始化方法会在构造函数创建实例后自动调用, 且将实例自身通过第一个参数 self 传入 __init__ 方法 构造函数的实参将通过 __init__ 方法的参数列表传到 __init__ 方法中 初始化方法内如果需要 return 语句返回, 则必须返回 None 示例: # 此示例示意初始化方法的定义方法和调用过程 class Car: """小汽车类""" def __init__(self, c, b, m): self.color = c # 颜色 self.brand = b # 品牌 self.model = m # 型号 self.wheel = 4 print("__init__方法被调用")def run(self, speed):
print(self.color, '的', self.brand, self.model, '正在以', speed, '公里/小时的速度行驶')def change_color(self, c):
self.color = c a4 = Car('红色', '奥迪', 'A4') a4.run(199) a4.change_color("白色") a4.run(280) x5 = Car('蓝色', '宝马', 'x5') x5.run(188)10. 析构方法:
作用: 在对象被销毁之前被调用,主要负责清理对象所占用的资源 语法形式: class 类名: def __del__(self): 语句块 说明: Python建议尽可能少的在析构方法内做事情, 因为销毁时间难以确定 示例: class FileManage: """定义一个文件管理员类""" def __init__(self, filename='a.txt'): self.file = open(filename, 'w')def writeline(self, string):
self.file.write(string) self.file.write('\n')def __del__(self):
"""析构方法会在对象销毁前自动调用""" self.file.close() print("文件已关闭") fm = FileManage() fm.writeline("hello world") fm.writeline("这是中文写的第二行") del fm while True: # 死循环永远不退出, del不调用一直不关闭文件 pass print("程序结束")11. 预置的实例属性:
__dict__ 属性: 用于绑定一个存储此实例自身变量的字典__class__ 属性:
用于绑定创建此实例的类 示例: class Dog: pass dog1 = Dog() print(dog1.__dict__) # {} dog1.color = "白色" print(dog1.__dict__) # {'color': '白色'} print(dog1.__class__) # <class '__main__.Dog'>12. 用于类的函数:
isintance(obj, class_or_tuple) 返回这个对象obj是否是某个类的对象, 或者某些类中的一个类的对象, 如果是则返回True, 否则返回False type(obj) 返回对象的类 对象: 属性(对象拥有的名词) 用实例变量存储 行为(对象拥有的动作) 用方法表示 13. 类变量: 类变量是类的属性, 此属性属于类, 不属于此类创建的实例 说明: 类变量,可以通过该类直接访问 类变量可以通过该类的实例直接访问 类变量可以通过此类的对象的 '__class__'属性间接访问 示例1: class Human: total_count = 0 # 类变量, 用于记录对象的个数 print(Human.total_count) h1 = Human() print(h1.total_count) # 0 # 不会出错 Human.total_count = 1 # 修改类变量 h1.total_count = 2 #添加了自己的实例属性total_count h1.__class__.total_count = 3 # 间接修改类变量 示例2: class Human: total_count = 0 # 类变量,用于记录对象的个数 def __init__(self, name): self.name = name self.__class__.total_count += 1 # 人数加1 print(name, "对象创建")def __del__(self):
self.__class__.total_count -= 1 # 总人数减1print("当前对象的个数是:", Human.total_count) # 0 h1 = Human("张飞") h2 = Human("赵云") print("当前对象的个数是:", Human.total_count) # 2 del h2 # 或 h2 = None print("当前对象的个数是:", Human.total_count) # 1 14. 类的 __slots__ 属性: 作用: 限定一个类创建的实例只能有固定的属性(实例变量) 说明: __slots__ 属性是一个列表, 列表的值是字符串 含有 __slots__ 属性的类所创建的对象没有__dict__字典 示例: class Student: # 限定此的类创建的对象只能有name和age两个属性 __slots__ = ['name', 'age']
def __init__(self, n, a):
self.name = n self.age = a s1 = Student("小张", 20) s1.Age = 21 # 此时是错写了age为Age, 会报错 print(s1.__dict__) # 出错,因为没有__dict__字典 15. 类方法 @classmethod: 类方法是操作类的方法, 类方法属于类, 不属于该类创建的对象 说明: 类方法需要使用@classmethod装饰器定义 类方法的第一个参数用来绑定类, 约定写为cls 类和对象实例都可以调用类方法 类方法不能访问此类创建的对象的属性 示例: class A: v = 0 # 类变量 @classmethod def get_v(cls): #此方法不是实例方法, 是类方法 return cls.v a = A() a.get_v() # 0 A.get_v() # 0 a.__dict__ # {} 16. 静态方法 @staticmethod: 静态方法是定义在类的内部的函数, 此函数作用域是类的内部 说明: 静态方法需要使用@staticmethod装饰器定义 静态方法与普通函数的定义相同, 不需要传入self和cls 静态方法只能凭借该类和实例来调用 静态方法不能访问类变量和实例变量 示例: class A: @staticmethod def myadd(a, b): return a + b print(A.myadd(100, 200)) # 300 a = A() print(a.myadd(300, 400)) # 30017. 继承 inheritance 和 派生 derived:
什么是继承 / 派生 继承是从已有类中派生出新类, 新类具有原类的数据属性和行为,并能扩展新的能力 派生就是从一个已有的类衍生出新类, 在新的类上添加新的属性和行为 作用: 01. 用继承派生机制, 可以将一些共有功能加在基类中, 实现代码的共享 02. 在不改变超类的代码的基础上, 改变原有的功能 名词: 基类(base class) / 超类(super class) / 父类(father class) 派生类(derived class) / 子类(child class) 单继承: 语法: class 类名(超类名): 语句块 示例: class Human: # 人 def say(self, what): # 说话的行为 print("说: ", what) def walk(self, distance): # 走路的行为 print("走了", distance, "公里") h1 = Human() h1.say("今天天气不错!") h1.walk(5)class Student(Human):
# def say(self, what): # 说话的行为 # print("说: ", what)# def walk(self, distance): # 走路的行为
# print("走了", distance, "公里")def study(self, subject):
print("正在学习", subject) s1 = Student() s1.say("今天晚饭吃什么?") s1.walk(3) s1.study("python") 继承说明: 任何类都直接或间接的继承自object类 object类是一切类的超类18. 类内的 __base__属性:
此属性用来记录此类的基类 示例: In [9]: class A: ...: pass ...:In [10]: class B(A):
...: pass ...:In [11]: B.__base__
Out[11]: __main__.A In [12]: A.__base__ Out[12]: object19. 覆盖: override:
什么是覆盖 覆盖是指在有继承派生关系的类中, 子类中实现了与基类(超类)同名的方法 在子类实例调用方法时, 实际调用的是子类中的覆盖版本, 这种现象叫做覆盖 示例: class A: def work(self): print("A类的work方法被调用")class B(A):
def work(self): print("B类的work方法被调用")b = B()
b.work() # 子类已经覆盖了父类的方法 20. super函数: super(type, obj) 返回绑定超类的实例(要求obj必须为type类型的实例) super() 返回绑定超类的实例, 等同于super(__class__, 实例的第一个参数), 且必须在方法内调用 作用: 返回超类的实例, 用超类实例来调用其自身的方法 说明: 当子类实现了__init__方法后, 父类的__init__方法将被覆盖 即不再会主动调用父类的__init__方法, 会引起父类的属性得不到初始化 此时需要显式调用父类的初始化方法 示例1: # 此示例示意用super函数访问父类的覆盖方法 class A: def work(self): print("A类的work方法被调用")class B(A):
def work(self): print("B类的work方法被调用")def doworks(self):
# self.work() # 调用B类的 super(B, self).work() # 调用超类的方法 super().work() # 一样会调用超类的方法 # super(__class__, self).work()b = B()
b.work() # B类的work方法被调用 print("-----以下用b调用覆盖版本的方法----") # A.work(b) # A类的work方法被调用 super(B, b).work() b.doworks() 示例2: # 此示例示意显式调用基类的初始化方法 class Human: def __init__(self, n, a): self.name = n self.age = a print("Human类的 __init__被调用")def show_info(self):
print("姓名:", self.name) print("年龄:", self.age) class Student(Human): """""" def __init__(self, n, a, s=0): super().__init__(n, a) # 显式调用基类的初始化方法 self.score = s print("Student类的 __init__被调用")def show_info(self):
super().show_info() print("成绩:", self.score) s1 = Student('coco', 20) s1.show_info()21. issubclass 函数:
issubclass(cls, class_or_tuple) 判断一个类是否是继承自其它的类, 如果此类cls是class或tuple中的一个派生子类则返回True,否则返回False 示例: class A: pass class B(A): pass class C(B): pass class D(B): pass issubclass(B, A) # True issubclass(C, B) # True issubclass(D, C) # False issubclass(C, (int, str)) False22. 封装 enclosure:
封装是指隐藏类的实现细节.让使用者不关心这些细节; 封装的目的是让使用者通过尽可能少的使用实例变量名(属性)操作对象 私有属性和方法 python类中以双下划线('__')开头, 不以双下划线结尾的标识符为私有成员 私有成员只能被方法调用, 不能在子类或其它地方使用 私有成员有两种: 私有属性 私有方法 示例: class A: def __init__(self): self.__p1 = 100 # 创建私有属性 def __m1(self): print("A类的私有方法被调用!") def test(self): print(self.__p1) # 可以访问 self.__m1() # 可以访问! a = A() # print(a.__p1) # 出错, 不可以访问 # a.__m1() # 出错, 在类外部不能调用类的私有方法 a.test() # 用方法来操作私有属性和私有方法 a.__dict__ # {'_A__p1': 100} a._A__p1 # 100 示例2: class Parent: def __func(self): print('in Parent func')def __init__(self):
self.__func() class Son(Parent): def __func(self): print('in Son func') son1 = Son() # in Parent func 23. 多态 polymorphic: 字面意思:多种状态 多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态 多态说明: 多态调用方法与对象相关, 不与类相关 python的全部对象只有"运行时状态(动态)", 没有"C++/Java"里的"编译时状态(静态)" 面向对象编程语言的特征: 封装 继承 多态 面向对象的语言: C++/Java/Python/Swift/C# 示例1: class Shape: def draw(self): passclass Point(Shape):
def draw(self): print("正在画一个点")class Circle(Point):
def draw(self): print("正在画一个圆") def my_draw(s): s.draw() # 在运行时动态决定调用的方法 s1 = Circle() s2 = Point() my_draw(s1) # 正在画一个圆 my_draw(s2) # 正在画一个点 示例2: class MiniOS(object): """MiniOS 操作系统类 """ def __init__(self, name): self.name = name self.apps = [] # 安装的应用程序名称列表def __str__(self):
return "%s 安装的软件列表为 %s" % (self.name, str(self.apps))def install_app(self, app):
# 判断是否已经安装了软件 if app.name in self.apps: print("已经安装了 %s,无需再次安装" % app.name) else: app.install() self.apps.append(app.name) class App(object): def __init__(self, name, version, desc): self.name = name self.version = version self.desc = descdef __str__(self):
return "%s 的当前版本是 %s - %s" % (self.name, self.version, self.desc)def install(self):
print("将 %s [%s] 的执行程序复制到程序目录..." % (self.name, self.version)) class PyCharm(App): pass class Chrome(App): def install(self): print("正在解压缩安装程序...") super().install() linux = MiniOS("Linux") print(linux)pycharm = PyCharm("PyCharm", "1.0", "python 开发的 IDE 环境")
chrome = Chrome("Chrome", "2.0", "谷歌浏览器")linux.install_app(pycharm)
linux.install_app(chrome) linux.install_app(chrome)print(linux)
24. 多继承 multiple inheritance:
多继承是指一个子类继承自两个或两个以上的基类 语法: class 类名(超类名1, 超类名2, ...): pass 示例: class Car: def run(self, speed): print("汽车以", speed, "km/h的速度行驶") class Plane: def fly(self, height): print("飞机以海拔", height, "米的高度飞行") class PlaneCar(Car, Plane): """飞行汽车类, 时继承 自Car和 Plane""" pass p1 = PlaneCar() p1.fly(10000) # 飞机以海拔 10000 米的高度飞行 p1.run(299) # 汽车以 299 km/h的速度行驶 多继承的问题(缺陷) 标识符(名字空间)冲突的问题 要谨慎使用多继承 示例: # 小张写了一个类A class A: def m(self): print("A.m()被调用") # 小李写了一个类B: class B: def m(self): print("B.m() 被调用") # 小王感觉小张和小李写的两个类自己可以用 class AB(A, B): pass ab = AB() ab.m() # 调用A类中的m方法 25. 多继承的 MRO (Method Resolution Order)问题: MRO 方法搜索顺序问题 python 3 广度优先: 一条路走到倒数第二级,判断,如果其他路能走到终点,则返回走另一条路.如果不能,则走到终点. python 2 深度优先: 一条路走到底. 单独调用父类的方法示例: print("******多继承使用类名.__init__ 发生的状态******") class Parent(object): def __init__(self, name): print('parent的init开始被调用') self.name = name print('parent的init结束被调用')class Son1(Parent):
def __init__(self, name, age): print('Son1的init开始被调用') self.age = age Parent.__init__(self, name) print('Son1的init结束被调用')class Son2(Parent):
def __init__(self, name, gender): print('Son2的init开始被调用') self.gender = gender Parent.__init__(self, name) print('Son2的init结束被调用')class Grandson(Son1, Son2):
def __init__(self, name, age, gender): print('Grandson的init开始被调用') Son1.__init__(self, name, age) # 单独调用父类的初始化方法 Son2.__init__(self, name, gender) print('Grandson的init结束被调用')gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name) print('年龄:', gs.age) print('性别:', gs.gender)print("******多继承使用类名.__init__ 发生的状态******\n\n")
多继承中super调用有所父类的被重写的方法示例: print("******多继承使用super().__init__ 发生的状态******") class Parent(object): def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数 print('parent的init开始被调用') self.name = name print('parent的init结束被调用')class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数 print('Son1的init开始被调用') self.age = age super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数 print('Son1的init结束被调用')class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数 print('Son2的init开始被调用') self.gender = gender super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数 print('Son2的init结束被调用')class Grandson(Son1, Son2):
def __init__(self, name, age, gender): print('Grandson的init开始被调用') # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍 # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因 # super(Grandson, self).__init__(name, age, gender) super().__init__(name, age, gender) print('Grandson的init结束被调用')print(Grandson.__mro__)
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name) print('年龄:', gs.age) print('性别:', gs.gender) print("******多继承使用super().__init__ 发生的状态******\n\n") 单继承中super示例: print("******单继承使用super().__init__ 发生的状态******") class Parent(object): def __init__(self, name): print('parent的init开始被调用') self.name = name print('parent的init结束被调用')class Son1(Parent):
def __init__(self, name, age): print('Son1的init开始被调用') self.age = age super().__init__(name) # 单继承不能提供全部参数 print('Son1的init结束被调用')class Grandson(Son1):
def __init__(self, name, age, gender): print('Grandson的init开始被调用') super().__init__(name, age) # 单继承不能提供全部参数 print('Grandson的init结束被调用')gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name) print('年龄:', gs.age) #print('性别:', gs.gender) print("******单继承使用super().__init__ 发生的状态******\n\n")26. 函数重写 overwrite:
什么是函数重写 在自定义的类中,通过添加特定的方法,让自定义的类生成的对象(实例) 能象内建对象一样进行内建函数操作 对象转字符串函数重写 repr(obj) 返回一个能代表此对象的字符串,通常:eval(repr(obj)) == obj str(obj) 通过给定的对象返回一个字符串(这个字符串通常是给人阅读的) 换句话说: repr(obj) 返回的字符串是给python用的 str(obj) 返回字符串是给人看的 示例: class B: def __str__(self): return 'str : class B'def __repr__(self):
return 'repr : class B' b=B() print('%s'%b) # str : class B print('%r'%b) # repr : class B27. 重写方法:
repr(obj) 函数的重写方法 def __repr__(self) str(obj) 函数的重写方法 def __str__(self) 当对象没有 __str__方法时, 则返回__repr__(self)的值 01. 内建函数重写: obj.__abs__() 方法对应 abs(obj) obj.__len__() 方法对应 len(obj) obj.__reversed__() 方法对应 reversed(obj) obj.__round__() 方法对应 round(obj) 示例: # 此示例示意abs(obj) 函数的重写方法 obj.__abs__() 方法的使用 class MyInteger: def __init__(self, value): self.data = valuedef __repr__(self):
return 'MyInteger(%d)' % self.datadef __abs__(self):
if self.data < 0: return MyInteger(-self.data) # 创建一个新的以对象并返回 return MyInteger(self.data)def __len__(self):
'''len(x)函数规定只能返回整数值, 因此此方法不能返回字符串等其它类型的值''' return 100I1 = MyInteger(-10)
print(I1) # <-- 此处等同于print(str(I1)) I2 = abs(I1) # I2 = MyInteger(10) print(I2) # MyInteger(10) print(len(I1)) # I1.__len__() 02. 数值转换函数重写: obj.__complex__() 对应 complex(obj) obj.__int__() 对应 int(obj) obj.__float__() 对应 float(obj) obj.__bool__() 对应 bool(obj) 03. 布尔测试函数的重写: 格式: def __bool__(self): pass 作用: 1. 用于bool(obj)函数取值 2. 用于if语句真值表达式中 3. 用于while语句真值表达式中 说明: 布测试式方法的查找顺序是 __bool__方法, 其次是__len__方法 如果没有以上方法则返回True 示例: # 此示例示意 bool 真值测试函数的重写 class MyList: '''定义一个容器类,用于存储任意类型的数据 其内部的存储方式用list实现 ''' def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%s)' % self.datadef __len__(self):
print("__len__ 方法被调用!") return len(self.data) # 返回列表的长度def __bool__(self):
print('__bool__方法被调用') '''此方法用于bool(obj) 函数取值,优先取此函 数的返回值,此方法用于定义bool(obj)的取值规则''' # 规则,所有元素的和为0,则返回False否则返回True return sum(self.data) != 0myl = MyList([1, -2, 5, -4])
print(myl) # MyList([1, -2, 5, -4]) print('bool(myl) =', bool(myl)) if myl: print('myl 的布尔值为True') else: print('myl 的布尔值为False') 04. 自定制格式化字符串__format__ : format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=typedef __repr__(self):
return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr)def __format__(self, format_spec):
# if format_spec if not format_spec or format_spec not in format_dict: format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj=self)s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1)'''
str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))28. 迭代器(高级):
什么是迭代器 可以通过 next(obj) 函数取值的对象, 就是迭代器 迭代器协议: 迭代器议是指对象能够使用next函数获取下一项数据, 在没有下一项数据时触发一个StopIteration异常来终止迭代的约定 迭代器协议的实现方法: 在类内需要定义 __next__(self)方法来实现迭代器协议 语法形式: class MyIterator: def __next__(self): 迭代器协议 return 数据 什么是可迭代对象 是指能用iter(obj)函数返回迭代器的对象(实例) 可迭代对象的内部要定义 __iter__(self)方法来返回迭代器对象 29. 属性管理函数(反射): getattr(obj, name[, default]) 从一个对象得到对象的属性;getattr(x, 'y') 等同于x.y; 当属性不存在时,如果给出default参数,则返回default,如果没有给出default 则产生一个AttributeError错误 hasattr(obj, name) 用给定的name返回对象obj是否有此属性,此种做法可以避免在getattr(obj, name)时引发错误 setattr(obj, name, value) 给对象obj的名为name的属性设置相应的值value, set(x, 'y', v) 等同于 x.y = v delattr(obj, name) 删除对象obj中的name属性, delattr(x, 'y') 等同于 del x.y 示例1: # 类反射 class Student: ROLE = 'STUDENT' @classmethod def check_course(cls): print('查看课程了')@staticmethod
def login(): print('登录') # 反射查看属性 print(Student.ROLE) # STUDENT print(getattr(Student,'ROLE')) # STUDENT# 反射调用方法
getattr(Student,'check_course')() # 查看课程了 getattr(Student,'login')() # 登录num = input('>>>') # login
if hasattr(Student,num): getattr(Student,num)() # 登录 示例2: # 对象放射 class Foo: f = '类的静态变量' def __init__(self,name,age): self.name=name self.age=agedef say_hi(self):
print('hi,%s'%self.name)obj=Foo('egon',73)
#检测是否含有某属性
print(hasattr(obj,'name')) print(hasattr(obj,'say_hi'))#获取属性
n=getattr(obj,'name') print(n) func=getattr(obj,'say_hi') func()print(getattr(obj,'aaaaaaaa','不存在啊')) #报错
#设置属性
setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj))#删除属性
delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_name111')#不存在,则报错print(obj.__dict__)
示例3: # 类反射 class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar') 示例4: # 反射当前模块成员 import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__]hasattr(this_module, 's1')
getattr(this_module, 's2') 示例5: # 导入其他模块,利用反射查找该模块是否存在某个方法 def test(): print('from the test') """ 程序目录: module_test.py index.py 当前文件: index.py """import module_test as obj
#obj.test()
print(hasattr(obj,'test'))
getattr(obj,'test')()
30. 异常(高级): with语句 语法: with 表达式1 [as 变量名1], 表达式2 [as 变量名2], ... 作用: 用于对资源访问的场合, 确保使用过程中不管是否发生异常, 都会执行必要有"清理"操作, 并释放资源. 如: 文件打开后自动关闭, 线程中锁的自动获取和释放 说明: with语句与try-finally相似, 并不会必变异常状态 as 子句用于绑定表达式创建的对象 示例: # 打开文件读取文件数据(with来实现关闭文件) def read_file(): try: # f = open("abcd.txt") with open('abcd.txt') as f: while True: s = f.readline() if not s: break # return int(input("请输入任意数字打印下一行:")) print(s) print("文件已经关闭")except IOError:
print("出现异常已经捕获!") except ValueError: print("程序已转为正常状态") read_file() print("程序结束")31. 环境管理器(上下文管理器):
1. 类内有__enter__ 和 __exit__方法的类被称为环境管理器 2. 能够用with进行管理的对象必须是环境管理器 3. __enter__ 将在进入with语句时被调用, 并返回由as变量管理的对象 4. __exit__ 将在离开with语句时被调用, 且可以用参数来判断离开with语句时是否出现异常并做出相应的处理 示例: # 本程序示意自定义的类作为环境管理器使用 class FileWriter: def __init__(self, filename): self.filename = filename # 此属性用于记住文件名def writeline(self, s):
'''此方法用于向文件内写入字符串,同时自动添加换行''' self.file.write(s) self.file.write('\n')def __enter__(self):
'''此方法用于实现环境管理器''' self.file = open(self.filename, 'w') print("已进入__enter__方法,文件打开成功") return self # 返回值向用于 with中的as 绑定def __exit__(self, exec_type, exec_value, exec_tb):
''' exec_type 为异常类异,没有异常发生时为None exec_value 为错误的对象,没有异常时为None exec_tb 为错误的traceback对象 ''' self.file.close() print("文件", self.filename, "已经关闭") if exec_type is None: print("退出with时没有发生异常") else: print("退出with时,有异常,类型是", exec_type, "错误是", exec_value) print("__exit__法被调用,已离开with语句") try: with FileWriter('log.txt') as fw: while True: s = input("请输入一行: ") if s == 'exit': break if s == 'error': raise ValueError("故意制造的值错误") fw.writeline(s) except: print("有错误发生,已转为正常")print("这是with语句之外,也是程序的最后一条语句")
32. 运算符重载: 什么是运算符重载 让自定义的类生成的对象(实例)能够使用运算符进行操作 作用: 让实例象数学表达式一样进行运算操作 让程序简洁易读 说明: 运算符重载方法的参数已经有固定的含义, 不建议改变原有的含义01. 算术运算符:
方法名 运算符 __add__ 加法 + __sub__ 减法 - __mul__ 乘法 * __truediv__ 除法 / __floordiv__ 地板除 // __mod__ 取模(求余) % __pow__ 幂 ** 二元运算符重载方法格式: def __xxx__(self, other): .... 示例: # 此程序示意运算符重载 class MyNumber: def __init__(self, v): self.data = vdef __repr__(self):
return "MyNumber(%d)" % self.datadef __add__(self, other):
print("__add__方法被调用") obj = MyNumber(self.data + other.data) return objdef __sub__(self, other):
return MyNumber(self.data - other.data) n1 = MyNumber(100) n2 = MyNumber(200) # n3 = n1.__add__(n2) n3 = n1 + n2 # 等同于 n1.__add__(n2) print(n3) n4 = n2 - n1 print('n4 =', n4) # MyNumber(100)02. 反向算术运算符:
方法名 运算符 __radd__ 加法 + __rsub__ 减法 - __rmul__ 乘法 * __rtruediv__ 除法 / __rfloordiv__ 地板除 // __rmod__ 取模(求余) % __rpow__ 幂 ** 示例: # 此示例示意返向算术运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%r)' % self.datadef __mul__(self, rhs):
return MyList(self.data * rhs)def __rmul__(self, lhs):
print("__rmul__被调用, lhs=", lhs) return MyList(self.data * lhs) # lhs (left hand side) L1 = MyList([1, 2, 3]) L2 = MyList(range(4, 7)) L4 = L1 * 2 # 实现乘法运算 print('L4 =', L4) # MyList([1,2,3,1,2,3]) L5 = 2 * L1 print(L5)03. 复合赋值运算符重载:
方法名 运算符 __iadd__ 加法 += __isub__ 减法 -= __imul__ 乘法 *= __itruediv__ 除法 /= __ifloordiv__ 地板除 //= __imod__ 取模(求余) %= __ipow__ 幂 **= 示例: # 此示例示意复合赋值算术运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%r)' % self.datadef __add__(self, rhs):
print("__add__方法被调用") return MyList(self.data + rhs.data)def __iadd__(self, rhs):
print("__iadd__方法被调用") self.data.extend(rhs.data) return selfL1 = MyList([1, 2, 3])
L2 = MyList(range(4, 7)) print("id(L1) =", id(L1)) L1 += L2 # 相当于 L1 = L1 + L2 print('L1 =', L1) print("id(L1) =", id(L1)) 问题: # 解法1 a = [100] def test(x): x = x + x print(x)test(a) # [100, 100]
print(a) # [100]# 解法2
a = [100] def test(x): x += x # 此处与上题不同。结果也会不同 print(x) test(a) # [100, 100] print(a) # [100, 100]04. 比较的运算符的重载:
__lt__ < 小于 __le__ <= 小于等于 __gt__ > 大于 __ge__ >= 大于等于 __eq__ == 等于 __ne__ != 不等于 注: 比较运算符通常返回True或False05. 位运算符重载:
__inert__ ~ 取反(一元运算符) __and__ & 位与(交集) __or__ | 位或(并集) __xor__ ^ 位异或(对称补集) __lshift__ << 左移 __rshift__ >> 右移06. 反向位运算符重载:
同算术运算符相同 __rand__ & 位与(交集) __ror__ | 位或(并集) __rxor__ ^ 位异或(对称补集) __rlshift__ << 左移 __rrshift__ >> 右移07. 复合赋值运算符重载:
__iand__ &= 位与(交集) __ior__ |= 位或(并集) __ixor__ ^= 位异或(对称补集) __ilshift__ <<= 左移 __irshift__ >>= 右移08. 一元运算符的重载:
__neg__ - 负号 __pos__ + 正号 __invert__ ~ 按位取反 格式: def __xxx__(self): .... 示例: # 此示例示意一元运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%r)' % self.datadef __neg__(self):
print("__neg__方法被调用!") L = (-x for x in self.data) return MyList(L) L1 = MyList([1, -2, 3, -4, 5]) print("L1 =", L1) L2 = -L1 print("L2 =", L2) 09. in / not in 运算符的重载: 格式: def __contains__(self, e): # e代表元素 ... 说明: not in 相当于 in取反, 所有只需要重载in 即可 # 此示例示意in / not in 运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%r)' % self.datadef __contains__(self, e): # e 代表测试元素
print("__contains__被调用") for x in self.data: if e == x: # 如果相同,则说明e在列表中 return True return False L1 = MyList([1, -2, 3, -4, 5]) if 2 in L1: # 需要重载 __contains__方法 print("2在L1中") else: print("2 不在L1中")10.索引和切片运算符的重载:
重载方法: __getitem__(self, i) 用于索引/切片取值 __setitem__(self, i) 用于索引/切片赋值 __delitem__(self, i) 用于del语句删除索引操作 作用: 让自定义的类型的对象能够支持索引和切片操作 示例: # 此示例示意索引 index 和切片 slice 运算符的重载 class MyList: def __init__(self, iterable): self.data = [x for x in iterable]def __repr__(self):
return 'MyList(%r)' % self.datadef __getitem__(self, i):
print("__getitem__被调用", i) return self.data[i]def __setitem__(self, i, v):
self.data[i] = vL1 = MyList([1, -2, 3, -4, 5])
print(L[0:2]) # [1, -2] print(L1[2]) # 3 L1[1] = 2 print(L1) # MyList([1, 2, 3, -4, 5]) 33. 组合: 给一个类的对象封装一个属性,这个属性是另一个类的对象 示例1: class GameRole: """英雄人物类""" def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hpdef attack(self,p):
p.hp = p.hp - self.ad print('%s 攻击 %s,%s 掉了%s血,还剩%s血' %(self.name,p.name,p.name,self.ad,p.hp))def armament_weapon(self,wea):
self.wea = wea class Weapon: """武器属性类""" def __init__(self,name,ad): self.name = name self.ad = ad def fight(self,p1,p2): p2.hp = p2.hp - self.ad print('%s 用%s打了%s,%s 掉了%s血,还剩%s血'\ % (p1.name,self.name,p2.name,p2.name,self.ad,p2.hp))p1 = GameRole('coco',20,500)
p2 = GameRole('cat',50,200) axe = Weapon('三板斧',60) broadsword = Weapon('屠龙宝刀',100) p1.armament_weapon(axe) # 给coco装备了三板斧这个对象. p1.wea.fight(p1,p2) 示例2: # 组合实现与类的关联 class School: def __init__(self, name, addr): self.name = name self.addr = addr class Course: def __init__(self, name, price, school): self.name = name self.price = price self.school = school s1 = School("coco", "成都") s2 = School("coco", "广州")msg = """
1 coco 成都 2 coco 广州 """while True:
print(msg) menu = { "1": s1, "2": s2 } choice = input("请选择学校>>>:") school_obj = menu[choice]name = input("课程名:")
price = input("课程费用:")new_course = Course(name, price, school_obj)
print("课程 %s 属于 %s 学校" % (new_course.name, new_course.school.name)) 34. 接口类, 抽象类: 定制一个规范, 让其子类必须按照此规范实现 示例: from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # 抽象类(接口类): @abstractmethod def pay(self): # 制定了一个规范 pass class Alipay(Payment): def __init__(self,money): self.money = moneydef pay(self):
print('使用支付宝支付了%s' %self.money) class Jdpay(Payment): def __init__(self, money): self.money = moneydef pay(self):
print('使用京东支付了%s' % self.money)class Wechatpay(Payment):
def __init__(self,money):
self.money = moneydef pay(self):
print('使用微信支付了%s' % self.money) def pay(obj): obj.pay() w1 = Wechatpay(200) a1 = Alipay(200) j1 = Jdpay(100) pay(a1) # 归一化设计 pay(j1) 35. 静态属性: 将方法伪装成一个属性,代码上没有什么提升,只是更合理 语法: @property @属性名.setter @属性名.deleter 示例1: class Person: def __init__(self,name,age): self.name = name if type(age) is int: self.__age = age else: print( '你输入的年龄的类型有误,请输入数字') @property def age(self): return self.__age@age.setter
def age(self,a1): '''判断,你修改的年龄必须是数字''' if type(a1) is int: self.__age = a1 else: print('你输入的年龄的类型有误,请输入数字')@age.deleter
def age(self): del self.__age p1 = Person('帅哥',20) print(p1.age) # 20 print(p1.__dict__) # {'name': '帅哥', '_Person__age': 20} p1.age = 23 print(p1.age) # 23 del p1.age示例2:
# 使用property取代getter和setter方法 class Money(object): def __init__(self): self.__money = 0# 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
@property def money(self): return self.__money# 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
@money.setter def money(self, value): if isinstance(value, int): self.__money = value else: print("error:不是整型数字")a = Money()
a.money = 100 print(a.money) 36. 单例: 如果一个类 从头到尾只能有一个实例,说明从头到尾之开辟了一块儿属于对象的空间,那么这个类就是一个单例类 示例1: class Single: __ISINCTANCE = None def __new__(cls, *args, **kwargs): if not cls.__ISINCTANCE: cls.__ISINCTANCE = object.__new__(cls) return cls.__ISINCTANCE def __init__(self,name,age): # self.name = name # self.age = age print(self) s1 = Single('coco',23) s2 = Single('gogo',40) 示例2: class MusicPlayer(object):# 记录第一个被创建对象的引用
instance = None# 记录是否执行过初始化动作
init_flag = Falsedef __new__(cls, *args, **kwargs):
# 1.判断类属性是否是空对象
if cls.instance is None: # 2.调用父类方法,为第一个对象分配空间 cls.instance = super().__new__(cls)# 3.返回类属性保存的对象引用
return cls.instancedef __init__(self):
# 1.判断是否执行过初始化动作
if MusicPlayer.init_flag: return # 2.没有执行过,再执行初始化动作 print("初始化播放器") # 3.修改类属性标记 MusicPlayer.init_flag = True player1 = MusicPlayer() # 初始化播放器 print(player1) # <__main__.MusicPlayer object at 0x0000000006AA28D0> player2 = MusicPlayer() print(player2) # <__main__.MusicPlayer object at 0x0000000006AA28D0> 37. __new__ 构造方法: class Single: def __new__(cls, *args, **kwargs): # print('在new方法里') obj = object.__new__(cls) print('在new方法里',obj) return objdef __init__(self):
print('在init方法里',self)01.开辟一个空间,属于对象的
02.把对象的空间传给self,执行init 03.将这个对象的空间返回给调用者 obj = Single() single的new,single没有,只能调用object的new方法 new方法在实例化之后,__init__ 之前先执行new来创建一块空间 38. __call__ 方法: __call__ 相当于 对象() class A: def __call__(self, *args, **kwargs): print('执行call方法了') def call(self): print('执行call方法了') class B: def __init__(self,cls): print('在实例化A之前做一些事情') self.a = cls() self.a() print('在实例化A之后做一些事情') a = A() a() # 对象() == 相当于调用__call__方法 a.call()A()() # 类名()() ,相当于先实例化得到一个对象,再对对象(),==>和上面的结果一样,相当于调用__call__方法
B(A) 39. item 系列方法: item系列 和对象使用[]访问值有联系 示例1: obj = {'k':'v'} print(obj) # 字典的对象 print(obj['k'])class B:
def __getitem__(self, item): return getattr(self,item) def __setitem__(self, key, value): setattr(self,key,value*2) def __delitem__(self, key): delattr(self,key) b = B() # b.k2 = 'v2' # print(b.k2) b['k1'] = 'v1' # __setitem__ print(b['k1']) # __getitem__ del b['k1'] # __delitem__ print(b['k1']) 示例2: class Foo: def __init__(self,name): self.name=namedef __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 obj.key时,我执行 del f1['age'] # del obj[key]时,我执行 f1['name']='coco' print(f1.__dict__) # {'name': 'coco'} 40. __eq__ 方法: class A: def __init__(self,name,age): self.name = name self.age = age def __eq__(self, other): if self.name == other.name and self.age == other.age: return True a = A('gogo',83) aa = A('gogo',83) aa2 = A('gogo',83) aa3 = A('gogo',83) aa4 = A('gogo',83) print(a,aa) print(aa3 == aa == aa4) # ==这个语法 是完全和__eq__ 41. 面向对象设计: def dogs(name, gender, type): # 狗的属性 def init(name, gender, type): dog = { "name": name, "gender": gender, "type": type, "run": run } return dog# 狗的方法
def run(dog): print("一条狗%s正在跑" % dog["name"])return init(name, gender, type)
d1 = dogs("wangcai", "公", "中华田园犬") d2 = dogs("xiaoqiang", "公", "哈士奇") print("面向对象设计", d1) d1["run"](d1) print(d2["name"])# 面向对象编程-->用面向对象独有的class语法实现面向对象设计
class Dog: """定义一个狗类""" def __init__(self,name, gender, type): self.name = name self.gender = gender self.type = typedef run(self):
print("一条狗%s正在跑" % self.name) dog1 = Dog("wangcai", "公", "中华田园犬") dog2 = Dog("xiaoqiang", "公", "哈士奇")print("面向对象编程", dog1.__dict__) # __dict__ 查看属性字典
print(dog1.__dict__["type"]) # 中华田园犬# 创建出的实例只有数据属性,函数属性实际是调的类的函数属性
dog1.run() print(dog2.name)# 查看类的名字
print(Dog.__name__) # Dog # 查看文档说明 print(Dog.__doc__) # 定义一个狗类 # 查看第一个父类 print(Dog.__base__) # <class 'object'> # 查看所有父类,构成元组 print(Dog.__bases__) # (<class 'object'>,) # 查看类的属性,返回一个字典 print(Dog.__dict__) # 查看类定义所在的模块 print(Dog.__module__) # __main__ # 查看实例对应的类 print(Dog.__class__) # <class 'type'> 42. 静态属性, 类方法, 静态方法: @property @classmethod @staticmethod 综合示例: class Room(object): tag = 1def __init__(self, wight, length):
self.wight = wight self.length = length@property
def car_area(self): return self.length * self.wight + self.tag@classmethod
def tell_info(cls): print(cls) print("-->", cls.tag)@staticmethod
def run(a, b): print("%s %s 正在跑步" % (a, b)) # 静态属性:将类的函数属性@property装饰后,是对象以数据属性的方式调用 # 只和实例对象绑定-->即可以访问实例属性也可以访问类的属性 p1 = Room(10, 20) print("类属性", p1.tag) print("实例对象调用静态属性", p1.car_area)# 类方法:将类的函数属性@classmethod装饰后,不用创建实例对象,调用类的函数属性
# 只和类绑定-->可以访问类属性,不能访问实例属性 Room.tell_info()# 静态方法:将类的函数属性@staticmethod装饰后,不创建和创建实例对象,都可以调用类的函数属性
# 即不和类绑定也不和实例对象绑定-->不能访问类属性,也不能访问实例属性,是类的工具包 Room.run("co1", "co2") p2 = Room(20, 20) p2.run("co3", "co4") 43. 包装标准类型: 基于标准数据类型来定制我们自己的数据类型,新增/改写方法 # 包装=继承+派生 class List(list): # 定义一个方法取列表中间值 def show_midlle(self): min_index = int(len(self)/2) return self[min_index]# 重写列表的append方法,限制只允许添加字符串
def append(self, p_object): if type(p_object) is str: super().append(p_object) else: print("类型错误,只允许添加字符串")l1 = List("helloworld")
print(l1, type(l1)) print("列表%s的中间值是%s" %(l1, l1.show_midlle())) l1.append("coco") l1.append(123) print(l1) 44. 重写反射方法: class Foo(): x = 1 def __init__(self): self.y = 1def __getattr__(self, item):
print("你要找的%s属性不存在" % item)# 重写内置删除方法,限制删除条件
def __delattr__(self, item): print("执行了删除%s属性的操作" % item) self.__dict__.pop(item)# 重写内置设置方法,限制设置条件
def __setattr__(self, key, value): print("你执行了设置%s属性值为%s的操作" %(key, value)) self.__dict__[key] = valuef1 = Foo()
f1.x = 10 f1.name = "coco" # 设置属性时会触发__setattr__print(f1.z) # 属性不存在时,会自动触发__getattr__
del f1.y # 删除属性时,会触发__delattr__print(f1.__dict__)
45. 授权: 授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。 授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性 实现授权的关键点就是覆盖__getattr__方法 示例1: # 授权-->是组合的运用 class Open: def __init__(self,filename, mode="r", encoding="utf-8"): self.filename = filename self.file = open(filename, mode, encoding=encoding) self.mode = mode self.encoding = encodingdef write(self, line):
t = time.strftime("%Y-%m-%d %X") self.file.write("%s %s" % (t, line))def __getattr__(self, item):
return getattr(self.file, item, "没有找到%s" % item) f1 = Open("a.txt", "r+") print(f1.file) print(f1.reads) print(f1.read) f1.write("hello world\n") f1.seek(0) # 移动文件指针到文件开头 print("读文件%s内容是%s" % (f1.filename, f1.read())) 示例2: #加上b模式支持 import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): if 'b' in mode: self.file=open(filename,mode) else: self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode=mode self.encoding=encodingdef write(self,line):
if 'b' in self.mode: if not isinstance(line,bytes): raise TypeError('must be bytes') self.file.write(line)def __getattr__(self, item):
return getattr(self.file,item)def __str__(self):
if 'b' in self.mode: res="<_io.BufferedReader name='%s'>" %self.filename else: res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) return res f1=FileHandle('b.txt','wb') # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close() 示例3: class List: def __init__(self,seq): self.seq=seqdef append(self, p_object):
' 派生自己的append加上类型检查,覆盖原有的append' if not isinstance(p_object,int): raise TypeError('must be int') self.seq.append(p_object)@property
def mid(self): '新增自己的方法' index=len(self.seq)//2 return self.seq[index]def __getattr__(self, item):
return getattr(self.seq,item)def __str__(self):
return str(self.seq)l=List([1,2,3])
print(l) l.append(4) print(l) # l.append('3333333') #报错,必须为int类型print(l.mid)
#基于授权,获得insert方法
l.insert(0,-123) print(l) 46. 双下方法其它补充, 示例说明: 示例1: # isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(): def __getattr__(self, item): print("执行了getattr查找%s" % item)# 无论是否找到属性都会执行getattribute
def __getattribute__(self, item): print("执行了getattribute查找%s" % item) raise AttributeError("抛出异常") f1 = Foo() print(isinstance(f1, Foo)) # 判断对象是否是类实例化出来的 True# issubclass(sub, super)检查sub类是否是 super 类的派生类
class Bar(): # 只针对字典方式操作触发 def __getitem__(self, item): print("%s getitem" % item) return self.__dict__[item]# 只针对字典方式操作触发
def __setitem__(self, key, value): print("%s:%s setitem" % (key, value)) self.__dict__[key] = value# 只针对字典方式操作触发
def __delitem__(self, key): print("%s delitem" % key) self.__dict__.pop(key) print(issubclass(Bar, Foo)) # 判断子类是否是由父类继承来的 True f1.x # 执行了 __getattribute__ 查找x -->执行了 __getattr__ 查找x f2 = Bar() f2["name"] = "coco" print(f2["name"]) f2["age"] = "18" print(f2.__dict__) del f2["age"] print(f2.__dict__) 示例2: # __str__ 与 __repr__ 示例 class Foo1(): # 运用在print打印时触发 def __str__(self): return "自定义打印信息"# 运用在解释器时触发
def __repr__(self): return "自定义打印信息2" l2 = Foo1() print(l2) # 自定义打印信息 print(l2) # 自定义打印信息,__str__和 __repr__共存,print先找str没有再找repr 示例3: # __format__ 格式化方法示例 format_dic = { "ymd": "{0.year}{0.mon}{0.day}", "m-d-y": "{0.mon}-{0.day}-{0.year}", }class Date:
def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = daydef __format__(self, format_spec):
print("-->", format_spec) if not format_spec or format_spec not in format_dic: format_spec = "ymd" fm=format_dic[format_spec] return fm.format(self) d1 = Date(2019, 4, 13) print("{0.year}{0.mon}{0.day}".format(d1)) # 2019413print(format(d1, "ymd")) # 2019413
print(format(d1, "m-d-y")) # 4-13-2019示例4:
class Foo2: "文档描述" # __slost__取代了类的__dict__使实例化除的属性没有__dict__方法 __slots__ = ["name", "age"] class Foo3(Foo2): def __del__(self): print("回收实例时触发")def __call__(self, *args, **kwargs):
print("__call__方法让实例对象可以加()执行")f3 = Foo2
print("f3", f3.__slots__)# __doc__记录类的文档描述信息
print(Foo2.__doc__) # 文档描述# __doc__无法被子类继承,应为子类一旦定义就有属于自己的__doc__
print(Foo3.__doc__) # None print(Foo3.__dict__)# __module__查看f3来自于哪个模块
print(f3.__module__) # __main__ # __class__查看f3来自于哪个类 print(f3.__class__) # <class 'type'># __del__析构函数,回收实例时触发
f3 = Foo3()f3() # __call__方法让实例对象可以加()执行
示例5:
# 描述符__get__ __set__ __delete__ class Foo4(): def __get__(self, instance, owner): print("get")def __set__(self, instance, value):
print("set")def __delete__(self, instance):
print("delete")class Foo5(Foo4):
name = Foo4() f5 = Foo5() f5.name # get f5.name = 1 # set del f5.name # delete 示例6: # 上下文管理器 实现 __enter__ __exit__ class Open(): def __init__(self, name): self.name = namedef __enter__(self):
print("执行了enter") return selfdef __exit__(self, exc_type, exc_val, exc_tb):
print("执行了exit") print(exc_type) # 异常类型 print(exc_val) # 异常值 print(exc_tb) # 异常追踪信息 with Open("a.txt") as f: print(f) # <__main__.Foo6 object at 0x000000000358E0F0> print(f.name) # a.txt print("结束") # 结束 示例7: # __hash__ class A: def __init__(self): self.a = 1 self.b = 2def __hash__(self):
return hash(str(self.a)+str(self.b)) a = A() print(hash(a)) 示例8: # __len__ class B: def __len__(self): print(666)b = B()
len(b) # len 一个对象就会触发 __len__方法。class A:
def __init__(self): self.a = 1 self.b = 2def __len__(self):
return len(self.__dict__) a = A() print(len(a)) 示例9: # __iter__ 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__ class Foo(object): def __init__(self, sq): self.sq = sqdef __iter__(self):
return iter(self.sq)obj = Foo([11,22,33,44])
for i in obj:
print i 示例10: 分片操作 __getslice__ __setslice__ __delslice__ class Foo(object): def __getslice__(self, i, j): print('__getslice__', i, j)def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)def __delslice__(self, i, j):
print('__delslice__', i, j)obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__ 47. 描述符的应用与优先级: 描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议 __get__():调用一个属性时,触发 __set__():为一个属性赋值时,触发 __delete__():采用del删除属性时,触发 数据描述符:至少实现了__get__()和__set__() 非数据描述符:没有实现:__set__() 描述符应用示例: class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self, instance, owner): print('get方法') # print('instance参数【%s】' %instance) # print('owner参数【%s】' %owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance参数【%s】' % instance) # print('value参数【%s】' % value) # print('====>',self) if not isinstance(value,self.expected_type): # print('你传入的类型不是字符串,错误') # return raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type)) instance.__dict__[self.key]=value def __delete__(self, instance): print('delete方法') # print('instance参数【%s】' % instance) instance.__dict__.pop(self.key) class People: name=Typed('name',str) #t1.__set__() self.__set__() age=Typed('age',int) #t1.__set__() self.__set__() def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People("coco", 13, 13.3) print(p1.__dict__) 描述符的优先级: 类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()01. 类属性 > 数据描述符:
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...')class People:
name=Str() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age # 在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 # 描述符被定义成了一个类属性,直接通过类名也可以调用 People.name #调用类属性name,本质就是在调用描述符Str,触发了__get__()People.name='coco' # 赋值并没有触发__set__()
del People.name # del也没有触发__delete__() ''' 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 People.name # 调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() People.name='coco' # 直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,不会触发描述符的__set__() del People.name #同上 ''' 02. 数据描述符 > 实例属性: #描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...')class People:
name=Str() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age p1=People('angels',18)# 如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name='coco' p1.name print(p1.__dict__) # 实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 del p1.name 03. 实例属性 > 非数据描述符: class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级 #对实例的属性操作,触发的都是描述符的 r1=Room('厕所',1,1) r1.name r1.name='厨房'
class Foo:
def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级 #对实例的属性操作,触发的都是实例自己的 r1=Room('厕所',1,1) r1.name r1.name='厨房' 04. 非数据描述符 > 找不到: class Foo: def func(self): print('func被调用')def __getattr__(self, item):
print('找不到就调用getattr',item) f1=Foo()f1.xxx # 找不到就调用getattr xxx
48. 类装饰器应用: 01. 类的装饰器无参数示例: def decorate(cls): print('类的装饰器开始运行啦------>') return cls@decorate #无参:People=decorate(People)
class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salaryp1=People('egon',18,3333.3)
02. 类的装饰器有参数示例: 示例1: def typeassert(**kwargs): def decorate(cls): print('类的装饰器开始运行啦------>',kwargs) return cls return decorate @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salaryp1=People('egon',18,3333.3)
示例2:
class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s"%func.__name__) self.__func = func def __call__(self): print("---装饰器中的功能---") self.__func() #说明: #1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象 # 并且会把test这个函数名当做参数传递到__init__方法中 # 即在__init__方法中的属性__func指向了test指向的函数 #2. test指向了用Test创建出来的实例对象 #3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法 #4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用 # 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体 @Test def test(): print("----test---") test() showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--" 03. 描述符装饰器综合运用示例: class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self, instance, owner): print('get方法') # print('instance参数【%s】' %instance) # print('owner参数【%s】' %owner) return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') # print('instance参数【%s】' % instance) # print('value参数【%s】' % value) # print('====>',self) if not isinstance(value,self.expected_type): # print('你传入的类型不是字符串,错误') # return raise TypeError('%s 传入的类型不是%s' %(self.key,self.expected_type)) instance.__dict__[self.key]=value def __delete__(self, instance): print('delete方法') # print('instance参数【%s】' % instance) instance.__dict__.pop(self.key) def deco(**kwargs): def wrapper(obj): for key,val in kwargs.items(): print("-->", key, val) setattr(obj, key, Typed(key, val)) return obj return wrapper @deco(name=str, age=int) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('coco',13, 13.3) print(p1.__dict__) 49. 用描述符定制 @property, @classmethod, @staticmethod: 01. 定制@property: class Lazyproperty: def __init__(self,func): print('==========>',func) self.func=func def __get__(self, instance, owner): print('get') # print(instance) # print(owner) if instance is None: return self res=self.func(instance) setattr(instance,self.func.__name__,res) return res class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length # @property #area=property(area) @Lazyproperty #area=Lazypropery(area) def area(self): return self.width * self.length @property #test=property(test) def area1(self): return self.width * self.length r1 = Room("coco", 10, 10) # ==========> <function Room.area at 0x0000000006AC4C80>print(r1.area1) # 100
#先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法 print(r1.area) # 先打印get, 再打印100 #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算 print(r1.area) # 先打印100, 再打印get print(Room.area) # <__main__.Lazyproperty object at 0x0000000005AE52B0> print(Room.area1) # <property object at 0x00000000052F1458> 02. 定制@classmethod: class ClassMethod: def __init__(self,func): self.func=funcdef __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(): print('在这里可以加功能啊...') return self.func(owner) return feedbackclass People:
name='coco' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls): print('你好啊,world %s' %cls.name)People.say_hi()
p1=People()
p1.say_hi()class ClassMethod:
def __init__(self,func): self.func=funcdef __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(*args,**kwargs): print('在这里可以加功能啊...') return self.func(owner,*args,**kwargs) return feedbackclass People:
name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls,msg): print('你好啊,world %s %s' %(cls.name,msg))People.say_hi('每天都是充满希望的一天')
p1=People()
p1.say_hi('每天都是充满希望的一天') 03. 定制@staticmethod: class StaticMethod: def __init__(self,func): self.func=funcdef __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(*args,**kwargs): print('在这里可以加功能啊...') return self.func(*args,**kwargs) return feedbackclass People:
@StaticMethod# say_hi=StaticMethod(say_hi) def say_hi(x,y,z): print('------>',x,y,z)People.say_hi(1,2,3)
p1=People()
p1.say_hi(4,5,6) 50. 自定义元类: 元类就是用来创建这些类(对象)的,元类就是类的类, type就是Python在背后用来创建所有类的元类 作用: 拦截类的创建, 修改类, 返回修改之后的类 类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程 示例1: class MyType(type): def __init__(self, a, b, c): print(a) # Foo print(b) # () print(c) # {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x00000000056307B8>} print("元类构造执行完成") # 元类构造执行完成def __call__(self, *args, **kwargs):
print(args, kwargs) # ('coco', 18) {} obj = object.__new__(self) # object.__new__(Foo)-->f1 self.__init__(obj, *args, **kwargs) # Foo.__init__(f1, *args, **kwargs) return obj class Foo(metaclass=MyType): # Foo=MyType(Foo, "Foo", (), {})-->__init__ def __init__(self, name, age): self.name = name self.age = age f1 = Foo("coco", 18) print(f1) # <__main__.Foo object at 0x00000000055BF6A0> print(f1.__dict__) # {'name': 'coco', 'age': 18} 示例2: class MyType(type): def __init__(self, what, bases=None, dict=None): super(MyType, self).__init__(what, bases, dict)def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)self.__init__(obj)
class Foo(object):
__metaclass__ = MyTypedef __init__(self, name):
self.name = namedef __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象 obj = Foo() 示例3: class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而__init__只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 def __new__(cls, class_name, class_parents, class_attr): # 遍历属性字典,把不是__开头的属性名字变为大写 new_attr = {} for name, value in class_attr.items(): if not name.startswith("__"): new_attr[name.upper()] = value# 方法1:通过'type'来做类对象的创建
return type(class_name, class_parents, new_attr)# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法 # return type.__new__(cls, class_name, class_parents, new_attr)# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass): bar = 'bip'# python2的用法
# class Foo(object): # __metaclass__ = UpperAttrMetaClass # bar = 'bip' print(hasattr(Foo, 'bar')) # 输出: False print(hasattr(Foo, 'BAR')) # 输出:Truef = Foo()
print(f.BAR) # 输出:'bip' 51. 元类实现ORM: ORM释义: ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,即对象-关系映射,简称ORM 创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句 作用: ORM就是让开发者在操作数据库的时候,能够像操作对象时通过xxxx.属性=yyyy一样简单 示例1: # 通过元类简单实现ORM中的insert功能 class ModelMetaclass(type): def __new__(cls, name, bases, attrs): mappings = dict() # 判断是否需要保存 for k, v in attrs.items(): # 判断是否是指定的StringField或者IntegerField的实例对象 if isinstance(v, tuple): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v# 删除这些已经在字典中存储的属性
for k in mappings.keys(): attrs.pop(k)# 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) class User(metaclass=ModelMetaclass): uid = ('uid', "int unsigned") name = ('username', "varchar(30)") email = ('email', "varchar(30)") password = ('password', "varchar(30)") # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储 # 以上User类中有 # __mappings__ = { # "uid": ('uid', "int unsigned") # "name": ('username', "varchar(30)") # "email": ('email', "varchar(30)") # "password": ('password', "varchar(30)") # } # __table__ = "User" def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value)def save(self):
fields = [] args = [] for k, v in self.__mappings__.items(): fields.append(v[0]) args.append(getattr(self, k, None))args_temp = list()
for temp in args: # 判断入如果是数字类型 if isinstance(temp, int): args_temp.append(str(temp)) elif isinstance(temp, str): args_temp.append("""'%s'""" % temp) sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp)) print('SQL: %s' % sql) u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd') # print(u.__dict__) u.save() 示例2: # 抽取到基类中 class ModelMetaclass(type): def __new__(cls, name, bases, attrs): mappings = dict() # 判断是否需要保存 for k, v in attrs.items(): # 判断是否是指定的StringField或者IntegerField的实例对象 if isinstance(v, tuple): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v# 删除这些已经在字典中存储的属性
for k in mappings.keys(): attrs.pop(k)# 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) class Model(object, metaclass=ModelMetaclass): def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value)def save(self):
fields = [] args = [] for k, v in self.__mappings__.items(): fields.append(v[0]) args.append(getattr(self, k, None))args_temp = list()
for temp in args: # 判断入如果是数字类型 if isinstance(temp, int): args_temp.append(str(temp)) elif isinstance(temp, str): args_temp.append("""'%s'""" % temp) sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp)) print('SQL: %s' % sql) class User(Model): uid = ('uid', "int unsigned") name = ('username', "varchar(30)") email = ('email', "varchar(30)") password = ('password', "varchar(30)") u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd') # print(u.__dict__) u.save()