第八章 面向对象编程
8.1 面向对象概述
面向对象是一种编程的思想,是一种解决问题的思路。
在编程当中,有面向过程的编程思想和面向对象的编程思想。
面向过程:
- 面向过程主要专注在过程,主要关注在解决问题的步骤,将问题逐步分析去解决,但是代码重复内容较多
- 函数可以帮助在功能上做一定的整合
- 龟兔赛跑:
# 龟兔赛跑 import time DISTENCE = 500 name_t = "turtle" speed_t = 100 t1 = DISTENCE / speed_t name_r = "rabbit" speed_r = 10000 t2 = DISTENCE / speed_r
- 面向过程主要专注在过程,主要关注在解决问题的步骤,将问题逐步分析去解决,但是代码重复内容较多
面向对象:
- 对象本质上是一种容器,编写程序时:变量和函数;即数据和方法。
- 函数可以针对方法进行进行整合,对象可以对某一类数据和方法的一个封装。
- 例如:
- 做饭的数据:食材,调味品 做饭的方法:厨具
- 可以将做饭的数据跟做饭的方法做一个封装,称为做饭的对象。
- 洗衣服的数据:脏衣服 洗衣服的方法:洗衣机
- 可以将洗衣服的数据跟做饭的方法做一个封装,称为洗衣服的对象。
- 做饭的数据:食材,调味品 做饭的方法:厨具
- 对象本质上是一种容器,编写程序时:变量和函数;即数据和方法。
8.2 面向对象基础
8.2.1 类和对象的定义
定义:类是对象的模板,对象是类的实例,类是创建对象用的,对象是实际实现功能的。
- 定义规范要求:类的名称首字母必须大写,同时采用驼峰体的命名方式。
- 定义类:
class MammalAnimal: name = "Turtle" speed = 100 def run(self): print("i can run") def jump(self): print("i can jump")
类的命名空间
- 类体代码在创建的时候就会产生,
class MammalAnimal: name = "Turtle" speed = 100 def 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 = 100 def 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 = 100 def 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 = 100 def 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 = ip self.username = username self.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 os
class Files:
def __init__(self, path):
self.path = path
if 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 = name
self.age = age
def 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 = name
self.age = age
def 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 = name
self.age = age
def 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:
pass
class EggMinIn:
speak = "我有翅膀"
def egg_func(self):
print("我会下蛋")
class Duck(EggMinIn, Animal):
pass
class Chicken(EggMinIn, Animal):
def Egg(self):
print("我会下蛋")
speak = "我有翅膀"
pass
class 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 abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):
print("I am an Animal")
class HumanBeing(Animal):
def speak(self):
pass
class Dog(Animal):
def speak(self):
pass
class Duck(Animal):
def speak(self):
pass
obj = HumanBeing()
obj1 = Dog()
8.4 隐藏属性
8.4.1 定义
class Foo:
def __init__(self, name, age):
self.__name = name # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。
self.age = age
obj = Foo('张三', 21)
print(obj._Foo__name) # 隐藏后的属性如果一定要调用,采用该方式调用
通过对象.属性
的方式来获取属性值,而如果定义类的时候属性名之前加上__
,那么此时该属性成为隐藏属性,通过之前的方式调用则会失败,必须通过对象._类名__属性名
的方式可以调用,但是不建议该隐藏后又再次被调用。
8.4.2 作用
- 开发者决定用户能够访问哪些属性,用什么样的方式能访问属性。影藏属性在类的内部可以直接调用,通过变形后的属性名
类.__属性名
class Foo:
def __init__(self, name, age):
self.__name = name # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。
self.age = age
def get_name(self):
# print("看什么看,不准查看")
# return
print(self.__name)
def set_name(self, val):
if val == str(val):
self.__name = val
print(self.__name)
else:
print("更改的类型必须是字符串")
def del_name(self):
del self.__name
obj = 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 = ip
self.username = username
self.password = password
print(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 = x
def 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 = x
def 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 = x
def get_x(self):
print(self.__x)
def set_x(self,val):
self.__x = val
def del_x(self):
del self.__x
obj = Foo(10)
obj.get_x()
obj.set_x(22)
obj.get_x()
装饰器配合property
class Foo:
def __init__(self, x):
self.__x = x
@property
def x(self):
print(self.__x)
@x.setter
def x(self, val):
self.__x = val
print(self.__x)
@x.deleter
def x(self):
del self.__x
obj = 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 = name self.age = age def run(self): pass obj = Foo("张三", 21) print(obj.__dict__) # 查询对象的属性,返回字典 print(dir(obj)) # 查询对象的属性和方法 print(obj.name) # 执行该属性,如果该属性不存在则会报错。
- 反射的方法
- 相比于通过
对象.属性
的形式,或者对象.方法
的形式来进行调用,也可以通过反射功能,采用字符串
的类型事项相应多功能
- 相比于通过
- 实例一:获取类的属性
反射的方法:
- hasattr:判断一个属性是否在对象中存在
lass Foo: def __init__(self, name, age): self.name = name self.age = age def run(self): pass obj = Foo("张三", 21) res = hasattr(obj, "name") # obj是对象,"name"属性对应的字符串,判断,返回结果是True或者False print(res)
- gettattr:判断属性是否存在,如果存在返回一个值,不存在返回另一个值
class Foo: def __init__(self, name, age): self.name = name self.age = age def run(self): pass obj = Foo("张三", 21) res = getattr(obj, "name",None) # obj是对象,"name"属性对应的字符串,存在则返回name值,不存在则返回None print(res)
- setattr:为属性赋值
class Foo: def __init__(self, name, age): self.name = name self.age = age def run(self): pass obj = Foo("张三", 21) setattr(obj, "name", "李四") print(getattr(obj, "name"))
李四
- delattr删除属性
class Foo: def __init__(self, name, age): self.name = name self.age = age def run(self): pass obj = Foo("张三", 21) delattr(obj, "name") res = getattr(obj, "name", "None") print(res)
- 应用实例
class Foo: def __init__(self, name, age): self.name = name self.age = age def 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_module
r = import_module('random')
res = r.randint(1, 10)
print(res)
- 注意:通过import_mudule最多只能导入到模块的级别,不能单独导入模块下的类。
from importlib import import_module
r = 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 = name
self.age = age
def run(self):
pass
def jump(self):
pass
def __str__(self):
return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
obj = Animal('Bob', 18)
print(obj)
4. __del__
当执行删除对象的操作时,会自动的触发
__del__
执行默认在程序执行完成之后,会回收程序代码资源,等于说这个时候会将对象回收,也就意味着触发了方法
__del__
的执行。
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
pass
def jump(self):
pass
def __str__(self):
return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
def __del__(self):
print("程序终止了")
obj = Animal('Bob', 18)
- 但是对于系统的资源是不会被回收,如果执行代码的过程中打开了一个文件,那么可以在
__del__
的方法下关闭一个文件,防止文件一直占用系统资源。
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
self.f = open("config.py", "r") # 打开文件
print("文件打开完毕")
def run(self):
pass
def jump(self):
pass
def __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 = name self.age = age print("haha") def run(self): pass def jump(self): pass def __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 = name
self.age = age
print("haha")
def run(self):
pass
def jump(self):
pass
def __str__(self):
return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __call__(self, *args, **kwargs):
return 123
obj = Animal('Bob', 18)
res = obj()
print(res)
7. __getitem__
、__setitem__
、__delitem__
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
print("看到我这句,表示打印了__init__中的属性")
def run(self):
pass
def jump(self):
pass
def __str__(self):
return "这是一个动物的对象" # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __call__(self, *args, **kwargs):
return 123
def __getitem__(self, item):
print(item)
pass
def __setitem__(self, key, value):
print(key)
print(value)
def __delitem__(self, key):
print(key)
pass
obj = 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 = name
self.age = age
def run(self):
pass
def jump(self):
pass
def __str__(self):
return "这是一个动物的对象" # 该函数返回一个字符串,否则会报错,在打印对象的时候会调用
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
def __call__(self, *args, **kwargs):
return 123
def __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 = name
def __enter__(self):
return 5
pass
def __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 = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 5:
raise StopIteration
else:
return self.counter
obj = Foo()
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
- 示例二:自定义一个range方法:
class Foo:
def __init__(self, num):
self.counter = -1
self.num = num
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration
else:
return self.counter
obj = 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 = -1
self.num = num
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration
else:
return self.counter
class 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 = name
self.age = age
def 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 = name
self.age = age
def 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 = a1
else:
print("类名必须全大写")
class Animal(object, metaclass=MyMeta): # 将该类的父类设置为MyMeta类
def __init__(self, name, age):
self.name = name
self.age = age
def 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 = name
self.age = age
def 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)