一.类
接上篇
5. 封装 enclosure
-
封装是指隐藏类的实现细节,让使用者不用关心这些细节;
-
封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
-
Python的封装是假的(模拟的)封装
-
私有属性和方法
-
python类中以双下划线(
__
)开头,不以双下划线结尾的标识符为私有成员,私有成员只能使用方法来进行访问和修改-
以
__
开头的属性为类的私有属性,在子类和类外部无法直接使用 -
以
__
开头的方法为私有方法,在子类和类外部无法直接调用
-
-
6. 多态 polymorphic
-
什么是多态:
-
字面意思"多种状态"
-
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
-
-
状态:
-
静态(编译时状态)
-
动态(运行时状态)
-
-
多态说明:
-
多态调用的方法与对象相关,不与类型相关
-
Python的全部对象都只有"运行时状态(动态)", 没有"C++语言"里的"编译时状态(静态)"
-
7. 方法重写
果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法
函数重写
-
在自定义类内添加相应的方法,让自定义类创建的实例像内建对象一样进行内建函数操作
对象转字符串函数重写
-
对象转字符串函数重写方法
-
str() 函数的重载方法:
-
def __str__(self)
-
如果没有
__str__(self)
方法,则返回repr(obj)函数结果代替
-
-
-
内建函数重写
-
__abs__
abs(obj) 函数调用 -
__len__
len(obj) 函数调用 -
__reversed__
reversed(obj) 函数调用 -
__round__
round(obj) 函数调用
运算符重载
-
运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作
-
运算符重载的作用
-
让自定义类的实例像内建对象一样进行运算符操作
-
让程序简洁易读
-
对自定义对象将运算符赋予新的运算规则
-
-
运算符重载说明:
-
运算符重载方法的参数已经有固定的含义,不建议改变原有的意义
-
算术运算符重载
方法名 | 运算符和表达式 | 说明 |
---|---|---|
__add__(self, rhs) | self + rhs | 加法 |
__sub__(self, rhs) | self - rhs | 减法 |
__mul__(self, rhs) | self * rhs | 乘法 |
__truediv__(self, rhs) | self / rhs | 除法 |
__floordiv__(self, rhs) | self // rhs | 地板除 |
__mod__(self, rhs) | self % rhs | 取模(求余) |
__pow__(self, rhs) | self ** rhs | 幂 |
8. super函数
super() 函数是用于调用父类(超类)的一个方法。
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
super() 方法的语法:
在子类方法中可以使用super().add()调用父类中已被覆盖的方法
可以使用super(Child, obj).myMethod()用子类对象调用父类已被覆盖的方法
super().init()
super().__init__()
是 Python 中用于调用父类(基类)构造函数的一种方式。它通常用于子类的构造函数中,以确保父类的构造函数被正确调用和初始化。这在继承(inheritance)中尤为重要,因为父类的初始化代码可能包含设置实例变量或执行其他重要的初始化任务。
注释
-
Parent 类:
-
定义了一个构造函数
__init__()
,在构造函数中打印了一条消息,并初始化了一个属性parent_attribute
。
-
-
Child 类:
-
继承自
Parent
类。 -
在其构造函数
__init__()
中,首先调用了super().__init__()
。这行代码会调用Parent
类的构造函数,确保Parent
类的初始化逻辑被执行。 -
然后打印了一条消息,并初始化了一个属性
child_attribute
。
-
-
实例化 Child 类:
-
创建
Child
类的实例时,首先执行Parent
类的构造函数,打印 "Parent class constructor called",然后执行Child
类的构造函数,打印 "Child class constructor called"。
-
为什么使用 super().__init__()
?
-
代码重用:避免在子类中重复父类的初始化代码。
-
正确初始化:确保父类的初始化逻辑(如设置属性、分配资源等)被执行。
-
支持多重继承:在多重继承情况下,
super()
可以确保所有基类的构造函数都被正确调用。
在之后的深度学习方法中经常见 super().__init__()
的用法
"""
解决钻石问题
"""
class A:
def process(self):
print("A process")
class B(A):
def process(self):
print("B process")
A.process(self) # 手动调用process方法
class C(A):
def process(self):
print("C process")
A.process(self) # 手动调用process方法
class D(B,C):
def process(self):
print("D process")
B.process(self) # 手动调用process方法
C.process(self) # 手动调用process方法
d1 = D()
d1.process()
print(111111111111111111)
class A:
def process(self):
print("A process")
class B(A):
def process(self):
print("B process")
super().process() # 手动调用process方法
class C(A):
def process(self):
print("C process")
super().process() # 手动调用process方法
class D(B,C):
def process(self):
print("D process")
super().process() # 手动调用process方法
d1 = D()
d1.process()
二.Python迭代器与生成器
1. 迭代器 Iterator
什么是迭代器
-
迭代器是访问可迭代对象的工具
-
迭代器是指用 iter(obj) 函数返回的对象(实例)
-
迭代器可以用next(it)函数获取可迭代对象的数据
迭代器函数iter和next
函数 | 说明 |
---|---|
iter(iterable) | 从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象 |
next(iterator) | 从迭代器iterator中获取下一个记录,如果无法获取一下条记录,则触发 StopIteration 异常 |
迭代器说明
-
迭代器只能往前取值,不会后退
-
用iter函数可以返回一个可迭代对象的迭代器
迭代器的用途
-
迭代器对象能用next函数获取下一个元素
2. 生成器
生成器是在程序运行时生成数据,与容器不同,它通常不会在内存中保留大量的数据,而是现用现生成。
-
yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
-
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
-
每次使用 yield 语句生产一个值后,函数都将暂停执行,等待被重新唤醒。
-
yield 语句相比于 return 语句,差别就在于 yield 语句返回的是可迭代对象,而 return 返回的为不可迭代对象。
-
然后,每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句。
生成器可以用算法动态的生成数据
生成器有两种
-
生成器函数
-
生成器表达式
生成器函数
含有yield 语句的函数是生成器函数,此函数调用回返回一个生成器对象,生成器也是可迭代对象
yield 语句的语法
yield 表达式
生成器函数的优势是它们可以按需生成值,避免一次性生成大量数据并占用大量内存。此外,生成器还可以与其他迭代工具(如for循环)无缝配合使用,提供简洁和高效的迭代方式。
生成器表达式
-
语法:
( 表达式 for 变量 in 可迭代对象 [if 真值表达式])
[] 内容代表可以省略
-
作用
用推导式的形式创建一个生成器
# # L = list(range(10)) # it = iter(L) # print(iter(L)) # print(next(it)) # # # while True: # try: # print(next(it)) # # except: # print("迭代结束") # break # # # def myrange(stop): # i = 0 # while i < stop: # yield i # i += 1 # for x in myrange(10): # print(x) list01 = list(range(10)) def find01(): for item in list01: if item > 5: yield item def find02(): for item in list01: if item % 2 == 1: yield item """ 封装 把不同的点分出来 """ def condition01(item): return item > 5 def condition02(item): return item % 2 == 1 """ 继承-隔 """ def find(func): for item in list01: if func(item): yield item """ 做-多态 """ for data in find(condition02): print(data)
三.python 函数式编程
定义:用一系列函数解决问题。
-
函数可以赋值给变量,赋值后变量绑定函数。
-
允许将函数作为参数传入另一个函数。
-
允许函数返回一个函数。
1. 函数作为参数
-
将核心逻辑传入方法体,使该方法的适用性更广。
总结
1、什么时候使用函数式编程思想?
很多的逻辑或者说核心点是不变的,大多数就是一致的,这个时候我们就可以使用函数式编程思想,可以很好的去定位这个逻辑【函数 式编程思想相对于面向对象编程思想,它更接近于算法】。
2、函数式编程思想替代了面向对象思想?
如果需求中存在多个逻辑变化点时,可以使用类来进行,因为面向对象中存在继承、重写。而函数式编程思想则是将变化点提取到函数 中,实现简单的逻辑。
1.1.1 lambda 表达式
-
定义:是一种匿名函数
作用:
-- 作为参数传递时语法简洁,优雅,代码可读性强。
-- 随时创建和销毁,减少程序耦合度。
语法
# 定义: 变量 = lambda 形参: 方法体 # 调用: 变量(实参)
说明:
-- 形参没有可以不填
-- 方法体只能有一条语句,且不支持赋值语句。
1.1.2 内置高阶函数
定义:将函数作为参数或返回值的函数。
常用:
(1)map(函数,可迭代对象)
-
使用可迭代对象中的每个元素调用函数,将返回值作为新可迭代对象元素;返回值为新可迭代对象。
(2)filter(函数,可迭代对象)
-
根据条件筛选可迭代对象中的元素,返回值为新可迭代对象。
(3)sorted(可迭代对象, key=函数, reverse=True)
-
排序,返回值为排序后的列表结果。
(4)max(可迭代对象, key = 函数)
-
根据函数获取可迭代对象的最大值。
(5)min(可迭代对象,key = 函数)
-
根据函数获取可迭代对象的最小值。
class Meinv: list1 = [] def __init__(self,name,score,age,height): self.name = name self.score = score self.age = age self.height = height # 直接将实例对象存入列表中,目的想以实例属性的方法去访问每个对象 Meinv.list1.append(self) def __str__(self): return f"{self.name},{self.age},{self.score},{self.height}" girl01 = Meinv("阿珂",100,23,166) girl02 = Meinv("苏荃",92,32,154) girl03 = Meinv("双儿",90,25,178) girl04 = Meinv("小郡主",76,22,168) girl05 = Meinv("方怡",75,27,165) for i in map(lambda item: item.name,Meinv.list1): print(i) for i in filter(lambda item:item.score > 80,Meinv.list1 ): print(i) print(111111111) print(max(Meinv.list1,key = lambda item:item.age)) print(111111111) for i in sorted(Meinv.list1,key = lambda item:item.score): print(i)
2. 函数作为返回值
2.1 闭包 closure
什么是闭包?
-
闭包是指引用了此函数外部嵌套函数的变量的函数 闭包就是能够读取其他函数内部变量的函数。只有函数内部的嵌套函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数,同时这个函数又引用了外部的变量“。
-
在本质上,闭包是将内部嵌套函数和函数外部的执行环境绑定在一起的对象。
闭包必须满足以下三个条件:
-
必须有一个内嵌函数
-
内嵌函数必须引用外部函数中变量
-
外部函数返回值必须是内嵌函数。
思考全局变量和局部变量的区别
-
全局变量
-
一直存在,谁都可以访问和修改
-
-
局部变量
-
只是在调用时存在,只能在函数内部进行访问和修改
-
闭包的优缺点 优点
1. 逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑。 2. 方便调用上下文的局部变量。 3. 加强封装性,是第2点的延伸,可以达到对变量的保护作用。
使用闭包的注意点(缺点)
1. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包 2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
2.2 装饰器 decorators(专业提高篇)
什么是装饰器
-
装饰器是一个函数,主要作用是来用包装另一个函数或类
装饰器的作用:
-
在不修改被装饰的函数的源代码,不改变被装饰的函数的调用方式的情况下添加或改变原函数的功能。
函数装饰器的语法:
def 装饰器函数名(fn): 语句块 return 函数对象 @装饰器函数名 <换行> def 被装饰函数名(形参列表): 语句块
用函数装饰器替换原函数myfun
def mydeco(fn):
fn()
print("装饰器函数被调用了,并返回了fx")
def fx():
print("fx被调用了")
# return fn()
return fx
@ mydeco
def myfun():
print("函数myfun被调用")
myfun()
myfun()
当使用@mydeco
语法装饰myfun
函数时,实际上发生的是:
-
myfun
函数作为参数传递给了mydeco
装饰器。 -
在
mydeco
内部,首先调用了fn()
,即此时调用了myfun
函数,产生了输出:"函数myfun被调用"。 -
接着,打印了"装饰器函数被调用了,并返回了fx"。
-
然后,
mydeco
装饰器返回了新的函数fx
。
因此,此刻myfun
实际上被替换成了新的函数fx
。这样的行为正是Python装饰器的特性之一:装饰器可以修改函数的行为,甚至完全替换被装饰的函数。
2.2.1 基本装饰器
有参数的函数装饰器(在myfunc外加了一层)
def mydeco(fn):
def fx():
print("====这是myfunc被调用之前====")
ret = fn()
print("----这是myfunc被调用之后====")
return ret
return fx
@mydeco
def myfunc():
print("myfunc被调用.")
myfunc()
myfunc()
myfunc()
2.2.2 带参数的装饰器
def repeat(num):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # 应用装饰器,重复执行下面的函数3次
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 调用被装饰的函数
-
repeat
是一个接受参数的装饰器工厂函数,它返回一个装饰器。 -
decorator
是真正的装饰器,它接受一个函数func
作为参数。 -
wrapper
函数重复执行被装饰的函数num
次。 -
使用
@repeat(3)
应用装饰器,使greet
函数被执行3次。
注意:带参数的装饰器需要三层函数,def wrapper(args, kwargs) 传入的是被修饰的函数的参数。
2.2.3 装饰器链
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def exclamation(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + "!"
return wrapper
@exclamation
@uppercase
def say_hello(name):
return f"Hello, {name}"
greeting = say_hello("Bob")
print(greeting) # 输出 "HELLO, BOB!"
具体的执行过程如下:
-
装饰器是从内到外依次应用的。在你的代码中,首先应用的是
@uppercase
,然后是@exclamation
。 -
@uppercase
装饰器会先包裹say_hello
函数,然后@exclamation
装饰器再包裹已经被@uppercase
包裹过的函数。
步骤详细如下:
-
首先
@uppercase
包装say_hello
函数:-
调用
say_hello("Bob")
返回"Hello, Bob"
。 -
@uppercase
装饰器的wrapper
函数将结果转换为大写,变为"HELLO, BOB"
。
-
-
然后
@exclamation
包装已经被@uppercase
包装过的say_hello
函数:-
调用
wrapper
(即@uppercase
装饰器的wrapper
函数)返回"HELLO, BOB"
。 -
@exclamation
装饰器的wrapper
函数将结果加上一个感叹号,变为"HELLO, BOB!"
。
-
2.2.4 类装饰器
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Something is happening before the function is called.")
result = self.func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
@MyDecorator # 应用类装饰器
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Charlie") # 调用被装饰的函数
-
MyDecorator
是一个类装饰器,它接受一个函数func
作为参数并在__call__
方法中执行额外操作。 -
使用
@MyDecorator
应用类装饰器,它将包装say_hello
方法,使其在调用前后执行额外操作。 -
与基本装饰器类似