第八章 面向对象编程
8.1 面向对象概述
-
面向对象是一种编程的思想,是一种解决问题的思路。
-
在编程当中,有面向过程的编程思想和面向对象的编程思想。
-
面向过程:
- 面向过程主要专注在过程,主要关注在解决问题的步骤,将问题逐步分析去解决,但是代码重复内容较多
- 函数可以帮助在功能上做一定的整合
- 龟兔赛跑:
# 龟兔赛跑 import timeDISTENCE = 500 name_t = "turtle" speed_t = 100 t1 = DISTENCE / speed_tname_r = "rabbit" speed_r = 10000 t2 = DISTENCE / speed_r
- 面向过程主要专注在过程,主要关注在解决问题的步骤,将问题逐步分析去解决,但是代码重复内容较多
-
面向对象:
- 对象本质上是一种容器,编写程序时:变量和函数;即数据和方法。
- 函数可以针对方法进行进行整合,对象可以对某一类数据和方法的一个封装。
- 例如:
- 做饭的数据:食材,调味品 做饭的方法:厨具
- 可以将做饭的数据跟做饭的方法做一个封装,称为做饭的对象。
- 洗衣服的数据:脏衣服 洗衣服的方法:洗衣机
- 可以将洗衣服的数据跟做饭的方法做一个封装,称为洗衣服的对象。
- 做饭的数据:食材,调味品 做饭的方法:厨具
- 对象本质上是一种容器,编写程序时:变量和函数;即数据和方法。
-
8.2 面向对象基础
8.2.1 类和对象的定义
-
定义:类是对象的模板,对象是类的实例,类是创建对象用的,对象是实际实现功能的。
- 定义规范要求:类的名称首字母必须大写,同时采用驼峰体的命名方式。
- 定义类:
class MammalAnimal:name = "Turtle"speed = 100def run(self):print("i can run")def jump(self):print("i can jump")
-
类的命名空间
- 类体代码在创建的时候就会产生,
class MammalAnimal:name = "Turtle"speed = 100def run(self):print("i can run")def jump(self):print("i can jump")print(MammalAnimal.__dict__)
{‘module’: ‘main’, ‘name’: ‘Turtle’, ‘speed’: 100, ‘run’: <function MammalAnimal.run at 0x0000026AF8AE4D60>, ‘jump’: <function MammalAnimal.jump at 0x0000026AF8AE4C20>, ‘dict’: <attribute ‘dict’ of ‘MammalAnimal’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘MammalAnimal’ objects>, ‘doc’: None}
-
类的属性访问方式
- 例如访问类的名称属性
class MammalAnimal:name = "Turtle"speed = 100def run(self):print("i can run")def jump(self):print("i can jump")print(MammalAnimal.__dict__["name"]) print(MammalAnimal.name)
Turtle
Turtle- 对象的定义
class MammalAnimal:name = "Turtle"speed = 100def run(self):print("i can run")def jump(self):print("i can jump")cat = MammalAnimal() print(cat.dict) # 打印对象,显示结果第一行 cat.__dict__["name"] = "cat" cat.__dict__["speed"] = 10 print(cat.__dict__) # 打印对象的命名空间,显示结果第二行
{}
{‘name’: ‘cat’, ‘speed’: 10}
8.2.2 对象的产生过程以及self参数
-
对象产生的过程
- 创建一个空对象,类MammalAnimal见上一小节
cat = MammalAnimal()
-
在对象的命名空间中添加数据
- 方式一:
cat.__dict__["name"] = "cat" cat.__dict__["speed"] = 10
- 方式二:
__init__
方法
class MammalAnimal:def __init__(self):self.name = "Turtle"self.speed = 100def run(self):print("i can run")def jump(self):print("i can jump")obj = MammalAnimal() # 此处已经将obj当成参数传入到MammalAnimal()中,如果再写一个字符串进去,会显示已经传递了两个位置参数 print(obj.__dict__)
{‘name’: ‘Turtle’, ‘speed’: 100}
- 方式三:定制化对象的参数
class MammalAnimal:def __init__(self, *args): # 采用可变长位置参数传递对象的属性self.name = args[0]self.speed = args[1]def run(self):print("i can run")def jump(self):print("i can jump")obj = MammalAnimal("cat", 5) print(obj.__dict__) dog = MammalAnimal("dog", 9) print(dog.__dict__)
{‘name’: ‘cat’, ‘speed’: 18}
{‘name’: ‘dog’, ‘speed’: 9} -
将实例化好的对象返回出来
- 最终对象的命名空间里只包含实例属性,而具体的函数方法,时保存在类的命名空间当中,那么对象调用这些方法的时候,先在自己的命名空间中查找,如果没有,则再到类的命名空间查找。
class MammalAnimal:fly = "" # 类属性def __init__(self, *args):self.name = args[0] # 实例属性self.speed = args[1]def fly_cap(self):MammalAnimal.fly = "yes" def run(self): print("i can run")def jump(self):print("i can jump")cat = MammalAnimal("cat", 5) dog = MammalAnimal("dog", 9) print(cat.__dict__) cat.fly_cap() # 通过对象调用方法 print(cat.fly) print(cat.__dict__) MammalAnimal.run(cat) # 通过类执行cat对象的run方法
{‘name’: ‘cat’, ‘speed’: 5}
yes
{‘name’: ‘cat’, ‘speed’: 5}i can run
8.2.3 常见成员
- 属性:变量类型
- 实例属性:属性前面加了
self.
则为实例方法 - 类属性:属性前面没有加
self.
是类方法
- 实例属性:属性前面加了
- 方法:函数类型
- 绑定方法:
- 实例方法:函数上存在(self)参数则为实例方法,专门给对象使用
- 类方法:
- 非绑定方法:
- 静态方法:
- 绑定方法:
8.2.4 应用场景
-
通过对象封装数据
- 方法一:非对象调用数据
switch_info = {"CE1": {"ip": "192.168.1.1", "username": "admin", "password": "Huawei@121"},"CE2": {"ip": "192.168.1.2", "username": "admin", "password": "Huawei@122"},"CE3": {"ip": "192.168.1.3", "username": "admin", "password": "Huawei@123"},"CE4": {"ip": "192.168.1.4", "username": "admin", "password": "Huawei@124"}, } print(switch_info["CE2"]["ip"])
192.168.1.2
- 方式二:通过对象调用数据
class SwitchInfo:def __init__(self, ip, username, password):self.ip = ipself.username = usernameself.password = password CE1 = SwitchInfo("192.168.100.1","python","Huawei@123") CE2 = SwitchInfo("192.168.100.2","python","Huawei@123") CE3 = SwitchInfo("192.168.100.3","python","Huawei@123") CE4 = SwitchInfo("192.168.100.4","python","Huawei@123") print(CE2.ip) # 通过对象调用
192.168.100.2
-
在实例化时可以完成必要操作:
import osclass Files:def __init__(self, path):self.path = pathif not os.path.exists(self.path):os.makedirs(self.path)def open_file(self):pass
8.2.5 数据类型回顾
L1 = list([1, 2, 3, 4, 5]) # list是类;l1是对象;[1,2,3,4,5]是初始化的参数
L1.append(3) # 将自己作为第一参数加入方法append计算
print(L1)
list.append(L1, 5) # 直接通过类调用两个参数L1列表,添加值5
print(L1)
[1, 2, 3, 4, 5, 3]
[1, 2, 3, 4, 5, 3, 5]
8.3 三大特性
8.3.1 封装
- 封装是面向对象编程中最重要的特性
- 封装体现在两个方面:
- 封装数据:对象在实例化的过程当中,调用类中的
__init__
完成初始化,并将初始化好的数据存放在对象的内存空间中,方便之后进行调用。 - 封装方法:将某一个类的方法封装在类中,做一个整合,方便以后进行调用。
- 封装数据:对象在实例化的过程当中,调用类中的
8.3.2 继承
1. 基础
- 定义:在python中子类可以继承父类当中的类属性和方法(父类当中的类属性和方法依然属于父类,子类只是继承了而已)
- 父类和子类
- 基类和派生类
- 作用:为了方便在原有的类基础上进行扩展,不需要重复造轮子
- 经典类和形式类:
- 经典类就是默认没有继承object的类
- 形式类就是默认继承了object的类
- 目前来说,在Python 3.0中,我们使用的都是新式类,默认都继承了object
2. 一般继承
class Animal:def __init__(self,name,age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")def swimming(self):print("我会游泳")class Humanbeing(Animal):def swimming(self): # 父类中存在该方法,此时重写该方法,叫做方法的重写print("我不会游泳")obj = Humanbeing("张三",18)
print(obj.name)
print(obj.age)
obj.run()
obj.swimming()
-
方法在父类和子类中都存在,会优先调用子类当中的方法,如果说我们想要调用父类当中的方法,需要执行以下操作:
- 方式一:
class Humanbeing(Animal):def swimming(self): Animal.swimming(self) # 指明父类中的方法
- 方式二:
class Humanbeing(Animal):def swimming(self): super(Humanbeing, self).swimming()使用super函数调用父类中的函数swimming()
3. 多层继承
- 多层继承,父类Animal,子类Intelligence,子类的子类Humanbeing,代码如下:
class Animal: def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")def swimming(self):print("我会游泳")class Intelligence(Animal): # def study(self):print("我能学习")def control(self):print("我能控制自己")class Humanbeing(Intelligence):def swimming(self):super(Humanbeing, self).swimming()obj = Humanbeing("张三", 18)
print(obj.name)
print(obj.age)
obj.run()
obj.swimming()
obj.study()
张三
18
我会跑
我会游泳
我能学习
4. 多重继承
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")def swimming(self):print("我会游泳")def control(self):print("我不能控制自己")class Intelligence():def study(self):print("我能学习")def control(self):print("我能控制自己")class Humanbeing(Animal, Intelligence): # 如果两个父类中都存在相同的方法,则采用最左侧的Animal的方法作为方法使用def swimming(self):super(Humanbeing, self).swimming()obj = Humanbeing("张三", 18)
print(obj.name)
print(obj.age)
obj.run()
obj.swimming()
obj.study()
obj.control() # 该方法在两个父类中都存在,选择最左侧的类中方法执行
张三
18
我会跑
我会游泳
我能学习
我不能控制自己
- 继承顺序问题
- 当使用obj.成员时,首先在obj对象关联的类当中进行查找,没有则向父类中查找
- 当多重继承时,先继承左边的,在继承右边的。
- 如果说要打破继承规则,就要进行方法的重写,其中可以直接指定调用类中的方法,也可以使用super表示要调用哪个父类中的方法。
- 注意:需要关注当前的self是属于哪个类的对象
5. 钻石继承
- 场景:D继承B,C,B和C都继承A,此时构成一个钻石继承
class A:def f1(self):print("A")def f2(self):print("AA")def f3(self):print("AAA")class B(A):def f1(self):print("B")def f2(self):print("BB")class C(A):def f1(self):print("C")def f3(self): print("CC")class D(B, C): # 先继承B再继承C方法def f1(self):print("D")obj = D()
obj.f1()
obj.f2()
obj.f3()
D
BB
CC
- C3算法和MRO列表
- 关于继承查找的顺序问题,通过C3算法计算得到的,在Python当中 提供了一个叫做mro的方法,通过
类.mro
可以得到一个mro列表,里面存放的时继承的关系。
- 关于继承查找的顺序问题,通过C3算法计算得到的,在Python当中 提供了一个叫做mro的方法,通过
class A:def f1(self):print("A")def f2(self):print("AA")def f3(self):print("AAA")class B(A):def f1(self):print("B")def f2(self):print("BB")class C(A):def f1(self):print("C")def f3(self): print("CC")class D(B, C): # 先继承B再继承C方法def f1(self):print("D")print(D.mro()) # 查看该类的继承顺序
[<class ‘main.D’>, <class ‘main.B’>, <class ‘main.C’>, <class ‘main.A’>, <class ‘object’>]
6. Mixint机制
Mixint是一种规范,一个类继承了多个类,那么通过Minxint机制能够区分出哪个是主类,那个是附加性的。
class Animal:passclass EggMinIn:speak = "我有翅膀"def egg_func(self):print("我会下蛋")class Duck(EggMinIn, Animal):passclass Chicken(EggMinIn, Animal):def Egg(self):print("我会下蛋")speak = "我有翅膀"passclass Cat(Animal):pass
8.3.3 多态
多态其实指的就是一种食物的多种形态;水: 气体:水蒸气;液体:水;固体:冰
class Animal:def speak(self):print("我是动物,我会叫")class Humanbeing(Animal):def speak(self):super().speak()print("我是人,我的叫声是啊啊啊")class Dog(Animal):def speak(self):super().speak()print("我是狗,我的叫声是汪汪汪")class Duck(Animal):def speak(self):super().speak()print("我是鸭子,我的叫声是嘎嘎嘎")obj = Dog()
obj.speak()
-
定义了一个叫做Animal的类,Humanbeing,Dog,duck类,人、狗、鸭子继承了动物类,那么此时动物类里面定义了一个speak方法,就意味着他的所有子类都拥有了speak这个方法。
-
这么讲的含义是什么呢,就是说假设我们有一个类,他存在某些特征,那么之后我们在遇到一些类,只要他属于挪个类,那么他就不需要思考,就能直接使用这些特征。
-
此时的Animal,起到的作用,就是即便下面的子类当中没有定义这个方法,但是由于他们属于Animal这个类,因此也可以直接调用Speak方法。
-
但是在python当中,其实并不推崇这个约束方式,而是按照规范来创建类,假如定义一个类需要具备某些特征,那么我们就应该自主的吧这个特征加到定义的类里面。比如在这个例子当中,不是应为继承了Animal才有speak这个方法,而是以为属于Animal的子类,而Animal拥有speak的方法,所以也要在自己的类中定义speak方法。
-
鸭子类型:
- 如果已知鸟走起路来像鸭子,叫起来像鸭子,游泳像鸭子,那么他就是鸭子。
-
拓展:
import abcclass Animal(metaclass=abc.ABCMeta):@abc.abstractmethoddef speak(self):print("I am an Animal")class HumanBeing(Animal):def speak(self):passclass Dog(Animal):def speak(self):passclass Duck(Animal):def speak(self):passobj = HumanBeing()
obj1 = Dog()
8.4 隐藏属性
8.4.1 定义
class Foo:def __init__(self, name, age):self.__name = name # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。self.age = ageobj = Foo('张三', 21)
print(obj._Foo__name) # 隐藏后的属性如果一定要调用,采用该方式调用
通过对象.属性
的方式来获取属性值,而如果定义类的时候属性名之前加上__
,那么此时该属性成为隐藏属性,通过之前的方式调用则会失败,必须通过对象._类名__属性名
的方式可以调用,但是不建议该隐藏后又再次被调用。
8.4.2 作用
- 开发者决定用户能够访问哪些属性,用什么样的方式能访问属性。影藏属性在类的内部可以直接调用,通过变形后的属性名
类.__属性名
class Foo:def __init__(self, name, age):self.__name = name # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。self.age = agedef get_name(self):# print("看什么看,不准查看")# returnprint(self.__name)def set_name(self, val):if val == str(val):self.__name = valprint(self.__name)else:print("更改的类型必须是字符串")def del_name(self):del self.__nameobj = Foo("张三", 18)
obj.set_name(123)
更改的类型必须是字符串
8.5 成员相关补充
- 变量:
- 实例变量:实例中定义
- 类变量:类中定义
- 方法:
- 绑定方法:绑定给对象使用
- 类方法:
- 对象方法
- 非绑定方法
- 静态方法:
- 绑定方法:绑定给对象使用
8.5.1 类方法
- 类的最大方法使用来创建对象,因此类方法的使用场景比较局限性,存在一个场景需要通过类来调用而不需要通过对象,就可以使用类方法。
import config # 引入另一个文件中的代码,代码如下:# Switch_info = {"CE1": {"IP": "192.168.1.1", "username": "admin", "password": "Huawei@123"},
# "CE2": {"IP": "192.168.1.2", "username": "admin", "password": "Huawei@123"},
# "CE3": {"IP": "192.168.1.3", "username": "admin", "password": "Huawei@123"},
# "CE4": {"IP": "192.168.1.4", "username": "admin", "password": "Huawei@123"}, }class SwitchMonitor:def __init__(self, ip, username, password):self.ip = ipself.username = usernameself.password = passwordprint(self.ip, self.username, self.password)@classmethod # 加了该命令后,不在将对象当成第一个参数传函数中,而是将类当成第一个参数传递进来def config_info(cls, arg): # cls相当于类名,是一个类return cls(SwitchMonitor(config.Switch_info[arg]["IP"], config.Switch_info[arg]["username"],config.Switch_info[arg]["password"]))obj = SwitchMonitor.config_info("CE1")
192.168.1.1 admin Huawei@123
8.5.2 静态方法
class Foo:def __init__(self, x):self.x = xdef f1(self):print(self.x ** 2)def f2(self):print(self.x ** 3)obj = Foo(10)
obj.f1()
obj.f2()
8.5.2 property
- 使用装饰器:
class Foo:def __init__(self, x):self.x = xdef f1(self):print(self.x ** 2)@property # 装饰器实现的功能,即可以通过调用属性的方式调用方法。def f2(self):print(self.x ** 3)obj = Foo(10)
obj.f2 # 调用属性的方法,如果是调用方法则obj.f2()
- 配合匿名属性使用
class Foo:def __init__(self, x):self.__x = xdef get_x(self):print(self.__x)def set_x(self,val):self.__x = valdef del_x(self):del self.__xobj = Foo(10)
obj.get_x()
obj.set_x(22)
obj.get_x()
装饰器配合property
class Foo:def __init__(self, x):self.__x = x@propertydef x(self):print(self.__x)@x.setterdef x(self, val):self.__x = valprint(self.__x)@x.deleterdef x(self):del self.__xobj = Foo(10)
obj.x = 20 # 像对对象属性一样的进行赋值,格式为x = val,则会自动匹配x.setter这个装饰器进行执行
del obj.x # 删除参数只要前面加了del,则匹配x.deleter删除参数
obj.x # 参数已经被删除,执行该语句报错
8.6 反射
8.6.1 反射的基本原理
-
动态语言
- 定义变量时候,不需要指明变量的类型,那么就可以称为动态语言,动态语言在程序执行时,才会知道数据的类型。
-
反射的定义:
- 反射是指在程序执行过程中,能够动态的获取对象的属性以及方法,执行之前不知道对象中具体有哪些内容。需要有一种灵活或者自由的方式能够判断属性是否存在,以及怎样获取对象的属性以及方法。
- 在Python当中的体现就是能够通过字符串来获取属性和方法。
-
作用:
- 实例一:获取类的属性
- name这个属性是否存在与这个对象,如果不存在会报错。
- 需要一个更便捷的方式来获取对象中的属性和方法的信息。
- 以及判断如果属性存在如何调用以及如果属性不存在怎么处理。
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passobj = Foo("张三", 21) print(obj.__dict__) # 查询对象的属性,返回字典 print(dir(obj)) # 查询对象的属性和方法 print(obj.name) # 执行该属性,如果该属性不存在则会报错。
- 反射的方法
- 相比于通过
对象.属性
的形式,或者对象.方法
的形式来进行调用,也可以通过反射功能,采用字符串
的类型事项相应多功能
- 相比于通过
- 实例一:获取类的属性
-
反射的方法:
- hasattr:判断一个属性是否在对象中存在
lass Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passobj = Foo("张三", 21) res = hasattr(obj, "name") # obj是对象,"name"属性对应的字符串,判断,返回结果是True或者False print(res)
- gettattr:判断属性是否存在,如果存在返回一个值,不存在返回另一个值
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passobj = Foo("张三", 21) res = getattr(obj, "name",None) # obj是对象,"name"属性对应的字符串,存在则返回name值,不存在则返回None print(res)
- setattr:为属性赋值
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passobj = Foo("张三", 21) setattr(obj, "name", "李四") print(getattr(obj, "name"))
李四
- delattr删除属性
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passobj = Foo("张三", 21) delattr(obj, "name") res = getattr(obj, "name", "None") print(res)
- 应用实例
class Foo:def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("123")def choose_function(self):option = input("please input a function's name:") # 输入run,结果返回到res,res()就是run()。if hasattr(self, option):res = getattr(obj, option)res()else:print("Invalid function")obj = Foo("张三", 21) obj.choose_function()
8.6.2 import_module+反射
- import_module:可以以导入字符串的形式导入一个模块
from importlib import import_moduler = import_module('random')
res = r.randint(1, 10)
print(res)
- 注意:通过import_mudule最多只能导入到模块的级别,不能单独导入模块下的类。
from importlib import import_moduler = import_module('telnetlib')
r1 = import_module('telnetlib.Telnet') # 错误方式
8.7 内置方法
8.7.1 内置方法
- 满足一定情况自动执行的方法,不需要手动调用,也叫做魔法方法
8.7.2 使用内置方法
1. __init__
- 在对象执行实例化操作是后,自动触发执行
__init__
的方法
2. __dict__
- 在生成命名空间的时候,会自动触发
__dict__
的方法。
3. __str__
- 在执行
print(对象)
的时候会自动触发__str__
,必须为该方法顶一个字符串的返回值,否则会报错。
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用obj = Animal('Bob', 18)
print(obj)
4. __del__
-
当执行删除对象的操作时,会自动的触发
__del__
执行 -
默认在程序执行完成之后,会回收程序代码资源,等于说这个时候会将对象回收,也就意味着触发了方法
__del__
的执行。
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用def __del__(self):print("程序终止了")obj = Animal('Bob', 18)
- 但是对于系统的资源是不会被回收,如果执行代码的过程中打开了一个文件,那么可以在
__del__
的方法下关闭一个文件,防止文件一直占用系统资源。
class Animal:def __init__(self, name, age):self.name = nameself.age = ageself.f = open("config.py", "r") # 打开文件print("文件打开完毕")def run(self):passdef jump(self):passdef __str__(self):return def __del__(self):self.f.close()print("文件关闭了") # 关闭文件obj = Animal('Bob', 18)
5. __new__
-
创建对象的时候会用到的魔法方法
-
创建对象的过程:
- 创建一个空对象,通过
__new__
方法创建出来的。 - 通过
__init__
方法初始化对象,需要注意,一定要有一个空对象才会触发__init__
- 返回一个初始化好的对象
obj = Animal('Bob', 18) # 值传递给__init__方法
- 创建一个空对象,通过
-
创造空对象是自动触发
__new__
方法,并且返回好一个创建好的空对象- 对象
class Animal:def __init__(self, name, age):self.name = nameself.age = ageprint("haha")def run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用def __new__(cls, *args, **kwargs):return super().__new__(cls)obj = Animal('Bob', 18) # 实例化对象 print(obj) # 显示结果为空 print(dir(Animal))
6. __call__
- 在执行对象()的操作时,会自动触发
__call__
方法的执行,并且可以将__call__
return的结果当成返回值。 - Animal()触发的应该是Animal父类中的call方法,可以推断Animal()触发的应该是Animal父类中的
__call__
方法,也就说明了,我们在做对象实例的时候,默认触发的就是Animal父类中的__call__
方法。 - 创建对象的过程:
- 触发Animal父类当中的call方法的执行。
- 创建一个空对象。
- 通过
__init__
方法初始化对象。 - 返回一个初始化好的对象。
class Animal:def __init__(self, name, age):self.name = nameself.age = ageprint("haha")def run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用def __new__(cls, *args, **kwargs):return super().__new__(cls)def __call__(self, *args, **kwargs):return 123obj = Animal('Bob', 18)
res = obj()
print(res)
7. __getitem__
、__setitem__
、__delitem__
class Animal:def __init__(self, name, age):self.name = nameself.age = ageprint("看到我这句,表示打印了__init__中的属性")def run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用def __new__(cls, *args, **kwargs):return super().__new__(cls)def __call__(self, *args, **kwargs):return 123def __getitem__(self, item):print(item)passdef __setitem__(self, key, value):print(key)print(value)def __delitem__(self, key):print(key)passobj = Animal('Bob', 18) # Animal()触发的应该是Animal类中的call方法。
obj["gender"] = "male" # 该语句自动执行setitem的方法
obj["name"] # 自动触发geitem方法的执行
del obj["age"] # 自动触发delitem执行
8. __add__
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):passdef jump(self):passdef __str__(self):return "这是一个动物的对象" # 该函数返回一个字符串,否则会报错,在打印对象的时候会调用def __new__(cls, *args, **kwargs):return super().__new__(cls)def __call__(self, *args, **kwargs):return 123def __add__(self, other):print(other)obj1 = Animal('Bob', 18) # Animal()触发的应该是Animal类中的call方法。
obj2 = Animal('Alice', 19)
obj3 = obj1 + 3 # 触发__add__方法
print(obj3)
9. __enter__
、__exit__
-
打开文件时采用的方法(回顾):
- 方法一:open
f = open("config.py", "r") # 打开后,需要手工关闭。 print(f.read()) f.close()
- 方法二:With xxx as 语句
with open("config.py", "r") as f: # 打开后当退出with语句块的时候,关闭文件。print(f.read())
-
__enter__
、__exit__
方法- 首先使用with对象 as语句会自动线触发
__enter__
方法,然后获得一个返回值,赋值给f,接着执行with 对象as下对应的代码块,执行完毕之后触发__exit__
方法。 - 使用该方法文件的过程类似
- 首先使用with对象 as语句会自动线触发
class Foo:def __init__(self, name):self.name = namedef __enter__(self):return 5passdef __exit__(self, exc_type, exc_val, exc_tb):print("-------")obj = Foo("张三")
with obj as f:print(f)print(123)
10. __inter__
、__next__
- 生成器定义:
- 对象中存在
__iter__
和__next__
方法 __iter__
方法返回自身(对象)- 通过
__next__
提取,如果没有数据可以提取,那么抛出一个StopIteration的错误
- 对象中存在
- 示例一:创建一个生成器函数
class Foo:def __init__(self):self.counter = 0def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 5:raise StopIterationelse:return self.counterobj = Foo()
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
- 示例二:自定义一个range方法:
class Foo:def __init__(self, num):self.counter = -1self.num = numdef __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == self.num:raise StopIterationelse:return self.counterobj = Foo(5)
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
- 生成器对象
- 是由generator类实例化而来的,本质上生成器需要满足的条件跟迭代器一样。
- 可迭代对象,需要满足两个条件。
- 内部拥有
__iter__
方法 - 通过
__iter__
方法返回一个迭代器对象
- 内部拥有
- 示例:只要是可迭代的对象都能使用for循环进行遍历,而只有迭代器对象和生成器对象才能使用next方法来提取数据。
class Foo:def __init__(self, num):self.counter = -1self.num = numdef __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == self.num:raise StopIterationelse:return self.counterclass Xoo:def __iter__(self):return Foo(100)obj = Xoo()
for i in obj:print(i)
8.8元 类
8.8.1 元类定义
- 元类就是类所属的类,python中一切皆对象,那么Animal这个类本质上也是对象,Animal这个对象是由哪个类实例化得到的呢。
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")obj = Animal("cat", 8)
print(type(obj)) # 显示结果obj类的父类是Animal
print(type(Animal)) # 显示结果Animal的父类是type,时一个元类。
<class ‘main.Animal’>
<class ‘type’>
8.8.2 自定义类
cls_name = "Animal"
cls_base = (object,)
cls_content = """
class Animal:def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")
"""
cls_namespace = {}
exec(cls_content, {}, cls_namespace)
Animal = type(cls_name, cls_base, cls_namespace) # 通过元类创造对象Animal,第一个参数是字符串,第二个参数是元组类型,第三个参数是字典类型
obj = Animal("张三", 18)
print(obj.__dict__)
8.8.3 自定义元类来对类的定义进行约束
class MyMeta(type):def __init__(self, a1, a2, a3): # a1代表类名,a2代表继承的父类,a3代表命名空间。if a1.isupper():self.a1 = a1else:print("类名必须全大写")class Animal(object, metaclass=MyMeta): # 将该类的父类设置为MyMeta类def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")obj = Animal("cat", 8)
print(obj)
8.8.4 在看__new__
和__call__
class MyMeta(type):def __call__(self, *args, **kwargs):print(args, kwargs)obj = self.__new__(self) # 创建空对象,对于MyMeta而言,self是Animal,调用Animal中的__new__方法,如果Animal中不存在__new__方法,则查找父类中的__new__方法。self.__init__(obj, *args, **kwargs) # 初始化参数return obj # 返回初始化好的对象class Animal(object, metaclass=MyMeta): # 将该类的父类设置为MyMeta类def __init__(self, name, age):self.name = nameself.age = agedef run(self):print("我会跑")def jump(self):print("我会跳")def __new__(cls, *args, **kwargs): # 因此Animal中该方法也可以删除,删除后,将调用父类中的__new__函数,父类中没有,将调用到元类。return super().__new__(cls)obj = Animal("cat", 8) # 相当于执行父类中的__call__类型,进而调用
print(obj)