一、类和对象(下)
1、封装
封装是指隐藏类的实现细节,让使用者不用关心这些细节;
封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
Python的封装是假的(模拟的)封装
私有属性和方法
-
python类中以双下划线(
__
)开头,不以双下划线结尾的标识符为私有成员,私有成员只能使用方法来进行访问和修改-
以
__
开头的属性为类的私有属性,在子类和类外部无法直接使用 -
以
__
开头的方法为私有方法,在子类和类外部无法直接调用
-
class A:def __init__(self):self.__a = 100 # 私有属性self.b = 200 # 实例属性# 私有方法def __m1(self):print("私有方法")a = A()
print(a.b) # 200
print(a.a) # AttributeError: 'A' object has no attribute 'a'
a.__m1() # AttributeError: 'A' object has no attribute '__m1'
# 强行访问(不推荐)
print(a._A__a) # 100
class B(A):def __init__(self):A.__init__(self)def m2(self):# 子类也无法访问self.__m1()print(f"{self.__a}")b_z = B()
b_z.m2() # AttributeError: 'B' object has no attribute '_B__m1'
2、多态
定义
-
字面意思"多种状态"
-
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
状态
-
静态(编译时状态)
-
动态(运行时状态)
说明
-
多态调用的方法与对象相关,不与类型相关
-
Python的全部对象都只有"运行时状态(动态)", 没有"C++语言"里的"编译时状态(静态)"
class Shape:def draw(self):print("Shape的draw()被调用")class Point(Shape):def draw(self):print("正在画一个点!")class Circle(Point):def draw(self):print("正在画一个圆!")def my_draw(s):s.draw() # 此处显示出多态shapes1 = Circle()
shapes2 = Point()
# 调用Circle 类中的draw
my_draw(shapes1) # 正在画一个圆!
# 调用Point 类中的draw
my_draw(shapes2) # 正在画一个点!
3、方法重写
果父类方法的功能不能满足需求,可以在子类重写父类的方法
函数重写
在自定义类内添加相应的方法,让自定义类创建的实例像内建对象一样进行内建函数操作
对象转字符串函数重写
str() 函数的重载方法:
def __str__(self)
- 如果没有
__str__(self)
方法,则返回repr(obj)函数结果代替
class MyNumber:"此类用于定义一个自定义的类,用于演示str/repr函数重写"def __init__(self, value):"构造函数,初始化MyNumber对象"self.value = valuedef __str__(self):"转换为普通字符串"return f"{self.value}"n1 = MyNumber("一只猫")
n2 = MyNumber("一只狗")
print(n2) # 一只狗
内建函数重写
-
__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 | 幂 |
4、super函数
super() 函数是用于调用父类(超类)的一个方法。
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
super() 方法的语法:
在子类方法中可以使用super().add()调用父类中已被覆盖的方法
可以使用super(Child, obj).myMethod()用子类对象调用父类已被覆盖的方法
"""
钻石继承问题
D B A C
"""
class A:def process(self):print("A process")class B(A):def process(self):print("B process")# 防止子类同名覆盖# A.process(self) # 手动调用A的process方法super().process() # 调用super()方法class C(A):def process(self):print("C process")# 防止子类同名覆盖# A.process(self) # 手动调用A的process方法super().process() # 调用super()方法class D(B,C):def process(self):print("D process")# 防止子类同名覆盖# B.process(self) # 手动调用B的process方法# C.process(self)super().process() # 调用super()方法d = D()
# d.process() # D B A C A
d.process() # D B A C
# 在类的属性中使用super()
class Parent:def __init__(self, name):self.name = nameclass Child(Parent):def __init__(self, name, age):super().__init__(name) # 调用super()方法 则不用写父类名self.age = agedef print_info(self):print(self.name, self.age)child01 = Child("小明",10)
child01.print_info() # 小明 10
二、迭代器和生成器
1、迭代器
-
迭代器是访问可迭代对象的工具
-
迭代器是指用 iter(obj) 函数返回的对象(实例)
-
迭代器可以用next(it)函数获取可迭代对象的数据
迭代器函数iter和next
函数 | 说明 |
---|---|
iter(iterable) | 从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象 |
next(iterator) | 从迭代器iterator中获取下一个记录,如果无法获取一下条记录,则触发 StopIteration 异常 |
说明
-
迭代器只能往前取值,不会后退
-
用iter函数可以返回一个可迭代对象的迭代器
# 可迭代的对象
L = list(range(10))
it = iter(L)
print(next(it)) # 0# try-except 处理异常
while True:try:print(next(it))except:print("迭代结束")break
# 0 1 2 3 4 5 6 7 8 9 迭代结束
2、生成器
生成器和迭代器一样可以配套for循环语句使用。
yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
- yield 语句返回的是可迭代对象
- return 返回的为不可迭代对象
生成器函数
含有yield 语句的函数是生成器函数,此函数调用回返回一个生成器对象,生成器也是可迭代对象
语法:
yield 表达式
def myrange(stop):i = 0while i < stop:yield ii += 1for x in myrange(10):print(x)# 0 1 2 3 4 5 6 7 8 9
生成器表达式
语法
( 表达式 for 变量 in 可迭代对象 [if 真值表达式])
# [] 内容代表可以省略
三、函数式编程
定义:用一系列函数解决问题。
-
函数可以赋值给变量,赋值后变量绑定函数。
-
允许将函数作为参数传入另一个函数。
-
允许函数返回一个函数。
1、函数作为参数
将核心逻辑传入方法体,使该方法的适用性更广。
def fun01():print("fun01函数执行")# a = fun01() # fun01函数执行def fun02(func):print("fun02函数执行")func()fun02(fun01) # fun02函数执行 fun01函数执行
总结
1、使用场合
很多的逻辑或者说核心点是不变的,大多数就是一致的,这个时候就可以使用函数式编程思想,可以很好的去定位这个逻辑【函数式编程思想相对于面向对象编程思想,它更接近于算法】。
2、函数式编程&面向对象思想
如果需求中存在多个逻辑变化点时,可以使用类来进行,因为面向对象中存在继承、重写。而函数式编程思想则是将变化点提取到函数中,实现简单的逻辑。
1.1 lambda表达式
# 定义:
变量 = lambda 形参: 方法体# 调用:
变量(实参)
说明
- 形参没有可以不填
- 方法体只能有一条语句,且不支持赋值语句
1.2 内置高阶函数
定义:将函数作为参数或返回值的函数。
常用:
(1)map(函数,可迭代对象)
-
使用可迭代对象中的每个元素调用函数,将返回值作为新可迭代对象元素;返回值为新可迭代对象。
(2)filter(函数,可迭代对象)
-
根据条件筛选可迭代对象中的元素,返回值为新可迭代对象。
(3)sorted(可迭代对象, key=函数, reverse=True)
-
排序,返回值为排序后的列表结果。
(4)max(可迭代对象, key = 函数)
-
根据函数获取可迭代对象的最大值。
(5)min(可迭代对象,key = 函数)
-
根据函数获取可迭代对象的最小值。
class Gril:list_girl = [] # 类属性 存放信息def __init__(self,name,score,age,height):self.name = nameself.score = scoreself.age = ageself.height = height# print(self)Gril.list_girl.append(self)def __str__(self):return f"{self.name}-{self.score}-{self.age}-{self.height}"girl01 = Gril("阿珂", 100, 23, 168)
girl02 = Gril("苏荃", 92, 32, 170)
girl03 = Gril("双儿", 90, 25, 159)
girl04 = Gril("小郡主", 79, 22, 160)
girl05 = Gril("方怡", 75, 27, 165)
girl06 = Gril("建宁", 86, 25, 163)
girl07 = Gril("曾柔", 67, 24, 158)# print(girl01)
"""
lambda表达式
变量 = lambda 形参: 方法体
"""# 01 打印所有对象的名称
"""
map(函数,可迭代对象)使用可迭代对象中的每个元素调用函数,将返回值作为新可迭代对象元素;返回值为新可迭代对象
"""
for element in map(lambda item: item.name, Gril.list_girl):print(element)
print("---------------")# 02 获取所有颜值大于80的对象
"""
filter(函数,可迭代对象)根据条件筛选可迭代对象中的元素,返回值为新可迭代对象
"""
for element in filter(lambda item: item.score > 80, Gril.list_girl):print(element)
print("---------------")# 03 获取年龄最大的对象
"""
max(可迭代对象, key = 函数)根据函数获取可迭代对象的最大值
"""
print(max(Gril.list_girl, key=lambda item:item.age))
print("---------------")# 04 排序
"""
sorted(可迭代对象, key=函数, reverse=True)排序,返回值为排序后的列表结果
"""
for element in sorted(Gril.list_girl, key=lambda item:item.score, reverse=False):print(element)"""
阿珂
苏荃
双儿
小郡主
方怡
建宁
曾柔
---------------
阿珂-100-23-168
苏荃-92-32-170
双儿-90-25-159
建宁-86-25-163
---------------
苏荃-92-32-170
---------------
曾柔-67-24-158
方怡-75-27-165
小郡主-79-22-160
建宁-86-25-163
双儿-90-25-159
苏荃-92-32-170
阿珂-100-23-168
"""
2、函数作为返回值
2.1 闭包
定义
-
定义在一个函数内部的函数,同时这个函数又引用了外部的变量。
-
在本质上,闭包是将内部嵌套函数和函数外部的执行环境绑定在一起的对象。
满足条件:
-
必须有一个内嵌函数
-
内嵌函数必须引用外部函数中变量
-
外部函数返回值必须是内嵌函数。
def func01():a = 1def func02():print(a)return func02re = func01()
re() # 1
2.2 装饰器
定义
-
装饰器是一个函数,主要作用是来用包装另一个函数或类
作用
-
在不修改被装饰的函数的源代码,不改变被装饰的函数的调用方式的情况下添加或改变原函数的功能。
语法
def 装饰器函数名(fn):语句块return 函数对象@装饰器函数名 <换行>
def 被装饰函数名(形参列表):语句块
def print_func_name(func):def wrapper():# 新功能# __name__ 获取函数名 内置属性print(func.__name__)# 旧功能return func()return wrapper# 被装饰器函数
@print_func_name
def func01():print("func01已执行")return "motto"
@print_func_name
def func02():print("func02已执行")func02() # func02 func02已执行
func01() # func01 func01已执行
分类
基本装饰器 带参数的装饰器 装饰器链 类装饰器
"""
旧函数 统计值
新函数 功能是打印总执行时间
"""
import time
# 新函数
def all_time(func):def wrapper(*args, **kwargs):# time.time()方法可以获取当前时间start_time = time.time()# 调用旧函数result = func(*args, **kwargs)# 获得结束时间end_time = time.time()print("执行时间:", end_time - start_time)return result# 闭包 返回内部函数return wrapper# 旧函数
@all_time
def func01():sum_value = 0for i in range(100000000):sum_value += ireturn sum_valuefunc01() # 4.061215400695801# 输入参数的形式
@all_time
def func02(n):sum_value = 0for i in range(n):sum_value += ireturn sum_valuefunc02(1000000000) # 41.46981358528137