理想的类结构
@Property
装饰器
# 传统写法
class Square1:def __init__(self):self.__side = Nonedef get_side(self):return self.__sidedef set_side(self, side):assert side >= 0, '边长不能为负数!'self.__side = sidedef del_side(self):# del self.__sideself.__side = 0 # 这里并不打算真的去删除# Pythonic写法
class Square2:def __init__(self):self.__side = None@propertydef side(self):return self.__side@side.setterdef side(self, side):assert side >= 0, '边长不能为负数!'self.__side = side@side.deleterdef side(self):# del self.__sideself.__side = 0 # 这里并不打算真的去删除s = Square2()
s.side = 10
print(s.side) # 10
del s.side
print(s.side) # 0
公有化与私有化
# 在Python类里面,"属性和方法"默认都是公有的(public)
class Person:def __init__(self, name: str, age: int):self._name = name # _ 类似与 C++ 的 protected,特点:_name 可以在类内部使用,也可以被继承self.__age = age # __ 类似与 C++ 的 private,特点:__age 可以在类内部使用,但不可以被继承@propertydef name(self):return self._name@name.setterdef name(self, name):self._name = name@propertydef age(self):return self.__age@age.setterdef age(self, name):self.__age = nameclass Teacher(Person): # 继承Person类@propertydef info(self):return self._name, self.__age # 报错,不存在 __age 属性t = Teacher('Jack', 35)
print(t.info)
属性控制
# 属性控制,可用于拦截或日志
class Person:# 控制获取属性def __getattribute__(self, item):print(f'getting attribute [{item}]')return super().__getattribute__(item) # 调用父级(object)的方法# 控制设置属性def __setattr__(self, key, value):print(f'setting attribute [{key}] to [{value}]')super().__setattr__(key, value) # 调用父级(object)的方法person = Person()
person.name = 'Jack' # 触发"__setattr__"方法
print(person.name) # 触发"__getattribute__"方法"""
运行结果:setting attribute [name] to [Jack]
getting attribute [name]
Jack"""
MRO
"""
MRO: Method Resolution Order(方法解析顺序)
作用:用于类的继承树的方法搜索顺序在单继承的情况下,我们可以很好的理解"类的继承树的方法顺序"
但是在多继承下,"类的继承树的方法顺序"就显得尤为复杂了(在 Python3 里面,使用了C3线性化的算法进行排序)
通常情况下,我们可以使用类的 mro() 方法来直接查看"类的继承树的方法顺序"
"""class A(object):passclass B(object):passclass C(object):passclass D(object):passclass E(object):passclass K1(C, A, B):passclass K2(A, D):passclass K3(B, D, E):passclass Z(K1, K2, K3):passprint(Z.mro())
# [
# <class '__main__.Z'>,
# <class '__main__.K1'>,
# <class '__main__.C'>,
# <class '__main__.K2'>,
# <class '__main__.A'>,
# <class '__main__.K3'>,
# <class '__main__.B'>,
# <class '__main__.D'>,
# <class '__main__.E'>,
# <class 'object'>
# ]
可迭代对象
# 利用迭代对象进行迭代,本质是用时间换取空间
# 在迭代进行的那一刻,数据才被产生或处理,从而大幅降低了内存空间的占用print('\n-----------------------------')# 可迭代对象的迭代
lst = [66, 77, 88]
for num in lst:print(num, end=' ')print('\n-----------------------------')# 上面 for 循环的迭代原理如下
iter_obj = lst.__iter__()
print(iter_obj.__next__(), end=' ')
print(iter_obj.__next__(), end=' ')
print(iter_obj.__next__(), end=' ')print('\n-----------------------------')# 自定义可迭代对象
class Fibonacci: # 斐波那契数"""F0 = 0F1 = 1Fn = Fn-1 + Fn-2 (n >= 2)0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, ..."""def __init__(self, n):self.__n = nself.__previous = 0self.__current = 1self.__count = 0def __iter__(self):return selfdef __next__(self):if self.__count >= self.__n:raise StopIterationself.__count += 1return_value = self.__previousself.__previous, self.__current = self.__current, self.__previous + self.__currentreturn return_value# 自定义可迭代对象的迭代
for num in Fibonacci(12):print(num, end=' ')print('\n-----------------------------')"""
运行结果:-----------------------------
66 77 88
-----------------------------
66 77 88
-----------------------------
0 1 1 2 3 5 8 13 21 34 55 89
-----------------------------"""
__getitem__()
方法
"""
在创建一个可迭代的对象序列时,可以用__iter__()和__next__()方法
如果没有这两个方法,我们还可以通过__getitem__()方法和__len__()方法进行创建序列
如果__getitem__()方法和__len__()方法也没有的话,解释器会抛出 TypeError 的错误!而__getitem__()方法与上面的__iter__()方法正好相反
它是以空间换取时间:1.先占用内存,布置好数据2.随机查找的时间复杂度 O(1)
"""from datetime import date, timedeltaclass DateRange:def __init__(self, start_dt, end_dt):self.start_dt = start_dtself.end_dt = end_dtself._range_values = self._get_range_values()def _get_range_values(self):data = []current_dt = self.start_dtwhile current_dt <= self.end_dt:data.append(current_dt)current_dt += timedelta(days=1)return datadef __len__(self):print("You are using the method of len().")return len(self._range_values)def __getitem__(self, index):print("You are using the symbol of [] to get item.")return self._range_values[index]my_date_range = DateRange(date(2024, 6, 1), date(2024, 6, 3))
print(len(my_date_range))for my_date in my_date_range:print(my_date)"""
迭代原理:
在 DateRange 类中,尽管我们没有显式地定义__iter__(),但由于定义了__getitem__和__len__,因此你仍然可以在for循环中使用my_date_range对象。
Python 会自动为我们处理迭代过程,使用你定义的__getitem__来获取日期,直到索引超出由__len__指定的范围。
总结起来,for循环在 Python 中确实是通过迭代对象来工作的,但迭代对象不一定需要显式地定义__iter__()。
如果对象定义了__getitem__和__len__(并且表现出序列的特性),Python将为你提供一个隐式的迭代器,使得你可以在for循环中使用该对象。运行结果:You are using the method of len().
3
You are using the symbol of [] to get item.
2024-06-01
You are using the symbol of [] to get item.
2024-06-02
You are using the symbol of [] to get item.
2024-06-03
You are using the symbol of [] to get item."""
__contains__()
方法
class Product: # 产品def __init__(self, name: str, price: float, num: int) -> None:self.name = nameself.price = priceself.num = numclass Promotion: # 促销方案def __init__(self, lower_num: int, upper_num: int, rate: float) -> None:self.__lower_num = lower_numself.__upper_num = upper_numself.__rate = rate@propertydef rate(self) -> float:return self.__ratedef __contains__(self, product: Product) -> bool:return self.__lower_num <= product.num <= self.__upper_numdef get_total_price(products: [Product], promotions: [Promotion]) -> float:total_price = 0for product in products:promotion = [promotion for promotion in promotions if product in promotion][0] # in 触发 __contains__()方法total_price += product.price * promotion.ratereturn total_priceif __name__ == '__main__':top_promotion = Promotion(100, 199, 0.5) # 最好促销方案average_promotion = Promotion(50, 99, 0.8) # 一般促销方案none_promotion = Promotion(0, 49, 1.0) # 没有优惠的促销方案promotions = (top_promotion, average_promotion, none_promotion)products = (Product('cart', 89188.90, 188),Product('computer', 7999.99, 66),Product('toy', 13.60, 27),)total_price = round(get_total_price(products, promotions), 2)print(total_price) # 51009.04
动态处理对象属性
class Person:def __init__(self, name: str) -> None:self.name = namedef __getattribute__(self, attr):print("All operations that get attribute trigger this function to execute.")return super().__getattribute__(attr)def __getattr__(self, attr):print(f"You're trying to get attribute [{attr}], but the attribute is not defined.")return '空空如也'def __setattr__(self, key, value):print(f"You are setting the [{key}] attribute to [{value}].")super().__setattr__(key, value)person = Person('Jack')
print(person.name)
# You are setting the [name] attribute to [Jack].
# All operations that get attribute trigger this function to execute.
# Jackperson.age = 33
print(person.age)
# You are setting the [age] attribute to [33].
# All operations that get attribute trigger this function to execute.
# 33# 如果尝试获取没有定义过的属性,会触发 __getattr__() 方法
print(person.hobby)
# All operations that get attribute trigger this function to execute.
# You're trying to get attribute [hobby], but the attribute is not defined.
# 空空如也
__call__()
方法
class MyPrint:def __call__(self, text: str, end=''):print(text, end=end)return selfmy_print = MyPrint()
print(callable(my_print)) # True
my_print("你好呀")("我很好")("那就好")("都很好") # 你好呀我很好那就好都很好
__str__()
与__repr__()
class Person:def __init__(self, name: str, hobby: [str]) -> None:self.name = nameself.hobby = hobbydef __str__(self):return f"{self.name} like {','.join(self.hobby)}"def __repr__(self):return f"Person(name={self.name}, hobby={self.hobby}"person = Person('Jack', ['football', 'basketball'])
print(person) # 调用 __str__()方法
print(str(person)) # 调用 __str__()方法
print(repr(person)) # 调用 __repr__()方法"""
__str__()方法是用于普通人群、客户看
__repr__()方法是用于机器、开发人员看运行结果:Jack like football,basketball
Jack like football,basketball
Person(name=Jack, hobby=['football', 'basketball']"""
深拷贝
import copylist1 = [1, 2, 3, [4, 5]]
list2 = copy.deepcopy(list1)print(list1, id(list1))
print(list2, id(list2))# [1, 2, 3, [4, 5]] 2016821662784
# [1, 2, 3, [4, 5]] 2016821293952
import copyclass A:def __repr__(self):return f"A: {id(self)}"class B:def __init__(self, a: A) -> None:self.a = aself.b = selfself.c = 1def __repr__(self):return f"B: {id(self.a)}, {id(self.b)}, {id(self.c)}"a1 = A()
b1 = B(a1)memo = {}
b2 = copy.deepcopy(b1, memo=memo)print(a1)
print(b1)
print(b2)b2.c = 3print(a1)
print(b1)
print(b2)"""
运行结果:(内存地址)A: 2124853485392
B: 2124853485392, 2124853487120, 140716077503272
B: 2124853487056, 2124851868688, 140716077503272
A: 2124853485392
B: 2124853485392, 2124853487120, 140716077503272
B: 2124853487056, 2124851868688, 140716077503336"""
抽象类与方法
from abc import abstractmethod, ABCMeta# 抽象类
class Component(metaclass=ABCMeta):@abstractmethoddef bind_data(self): # 抽象方法pass@abstractmethoddef render(self): # 抽象方法pass# 具体类
class ProductListComponent(Component):def bind_data(self): # 具体实现return ['computer', 'life']def render(self): # 具体实现for item in self.bind_data():print(f'--- {item.upper()} ---')product_list_component = ProductListComponent()
product_list_component.render()
数据类
"""
我们知道,在C/C++/C#语言里面,都有结构体Struct,它作为数据的载体,我们使用它处理数据很方便
在python里面,同样提供了类似的东西,就是数据类————@dataclass
数据类,在Python3.7版本开始以标准库方式提供为什么要用数据类呢?
因为使用普通的类进行数据的封装与处理,往往需要经过很多繁琐且无意义的工作,比如使用__init__()方法声明数据等等
而使用数据类,将能帮助我们减少这些繁琐的操作,将精力集中在处理数据本身上
"""class Person:def __init__(self, name: str, age: int) -> None:self.name = nameself.age = agedef __repr__(self) -> str:return f"{self.name}({self.age})"obj1 = Person("Jack", 28)
obj2 = Person("John", 35)
print(obj1)
print(obj2)
print(obj1 == obj2)
# Jack(28)
# John(35)
# Falsefrom dataclasses import dataclass@dataclass
class People:name: strage: intobj1 = People("Jack", 28)
obj2 = People("John", 35)
print(obj1)
print(obj2)
print(obj1 == obj2)
# People(name='Jack', age=28)
# People(name='John', age=35)
# False
官方文档:https://docs.python.org/zh-cn/3/library/dataclasses.html
元类
# 所有类的祖宗————type# int类
num = 3
print(type(num))
print(type(type(num)))
print(type(type(type(num))))
# <class 'int'>
# <class 'type'>
# <class 'type'># str类
name = "Jack"
print(type(name))
print(type(type(name)))
print(type(type(type(name))))
# <class 'str'>
# <class 'type'>
# <class 'type'># bool类
flag = True
print(type(flag))
print(type(type(flag)))
print(type(type(type(flag))))
# <class 'bool'>
# <class 'type'>
# <class 'type'># Object类
print(type(object))
print(type(type(object)))# <class 'type'>
# <class 'type'># 自定义类
class MyClass:passobj = MyClass()print(type(obj))
print(type(type(obj)))
print(type(type(type(obj))))
print('=' * 16)
# <class '__main__.MyClass'>
# <class 'type'>
# <class 'type'>
# 通过类的祖宗type创建一个类(类对象)
# 1. 定义类里面要实现的方法
def __repr__(self):return f"{self.__class__.__name__}(wheel={self.wheel}, type={self.size})"# 2. 通过type()创建类对象
# --- 参数: ( 类的名称、(要继承的父类...)、{ 要定义的属性: ... } )
Vehicle = type('Vehicle', (), {'wheel': 4, '__repr__': __repr__, })
Bus = type('Bus', (Vehicle,), {'size': 'big', })
Truck = type('Truck', (Vehicle,), {'size': 'extremely big', })# 类的实例
bus = Bus()
truck = Truck()
print(bus)
print(truck)
# Bus(wheel=4, type=big)
# Truck(wheel=4, type=extremely big)
class MetaAutoAttributes(type):def __new__(cls, name, bases, attrs):# 在这里我们可以修改或添加attrs中的属性attrs['auto_attribute'] = 'This is an automatically added attribute'# 调用父类的__new__方法来创建类return super().__new__(cls, name, bases, attrs)# 使用自定义元类class MyClass(metaclass=MetaAutoAttributes):pass# 创建MyClass的实例
obj = MyClass()# 访问自动添加的属性
print(obj.auto_attribute) # 输出: This is an automatically added attribute
推荐阅读:https://www.cnblogs.com/JetpropelledSnake/p/9094103.html
函数装饰器
import timedef timer(func):start = time.perf_counter() # 统计时间func()end = time.perf_counter()print(f"耗时:{end - start}")def show():print('showing...')time.sleep(0.5)timer(show)# showing...
# 耗时:0.5006642000516877
import timedef timer(func):start = time.perf_counter() # 统计时间res = func()end = time.perf_counter()print(f"耗时:{end - start}")return resdef show():print('showing...')time.sleep(0.5)return "OK"result = timer(show)
print(result)# showing...
# 耗时:0.5005822000093758
# OK
import timedef timer(func):def inner(*args, **kwargs):start = time.perf_counter() # 统计时间res = func(*args, **kwargs)end = time.perf_counter()print(f"耗时:{end - start}")return resreturn innerdef show(name: str) -> str:print(f'{name} is showing...')time.sleep(0.5)return "OK"show = timer(show)
result = show('Jack')
print(result)
print(show.__name__) # 不完美的伪装# Jack is showing...
# 耗时:0.5001411000266671
# OK
# inner
import time
import functoolsdef timer(func):@functools.wraps(func) # 完美伪装def inner(*args, **kwargs):start = time.perf_counter() # 统计时间res = func(*args, **kwargs)end = time.perf_counter()print(f"耗时:{end - start}")return resreturn innerdef show(name: str) -> str:print(f'{name} is showing...')time.sleep(0.5)return "OK"show = timer(show)
result = show('Jack')
print(result)
print(show.__name__) # 完美伪装# Jack is showing...
# 耗时:0.5001411000266671
# OK
# show
import time
import functoolsdef timer(func):@functools.wraps(func)def inner(*args, **kwargs):start = time.perf_counter() # 统计时间res = func(*args, **kwargs)end = time.perf_counter()print(f"耗时:{end - start}")return resreturn inner@timer # 使用语法糖
def show(name: str) -> str:print(f'{name} is showing...')time.sleep(0.5)return "OK"# 不再使用原生的 show = timer(show) ,而是使用语法糖
result = show('Jack')
print(result)
print(show.__name__)# Jack is showing...
# 耗时:0.5002126999897882
# OK
# show
含参装饰器
import time
import functoolsdef timer(appearance=False): # 是否开启外观模式,默认不开启def outer(func):@functools.wraps(func)def inner(*args, **kwargs):if appearance:print("=" * 16)start = time.perf_counter() # 统计时间res = func(*args, **kwargs)end = time.perf_counter()print(f"耗时:{end - start}")if appearance:print("=" * 16)return resreturn innerreturn outer@timer(appearance=True)
def show(name: str) -> str:print(f'{name} is showing...')time.sleep(0.5)return "OK"result = show('Jack')
print(result)# ================
# Jack is showing...
# 耗时:0.5003457000711933
# ================
# OK
类装饰器
# 装饰功能:给被装饰的类,添加两个方法
class Entity:def __call__(self, cls):# 声明第一个方法def __repr__(self) -> str:return f'{cls.__qualname__}' # __qualname__ 用于获取类或函数的完全限定名称,即包括模块名称和嵌套结构# 声明第二个方法def is_adult(self) -> bool:return self.age > 18# 给被装饰的类,添加上面两个方法cls.__repr__ = __repr__cls.is_adult = is_adultreturn cls@Entity() # Entity() 实例化一个对象. 对象 + __call__() 方法 == 函数()
class User:def __init__(self, name, age):self.name = ageself.age = age# 本质:User = obj(User)
# 本质:User = clsuser = User('Jack', 35)
print(user)
print(user.is_adult())
# User
# True
上下文管理器
# 传统操作
file = open('test.txt', 'w')
try:file.write('This is a test file.')
except Exception as e:print(e)
finally:file.close()
# 利用上下文管理器with操作
with open('test.txt', 'w') as file:file.write('This is a test file.')# 查看是否已经关闭文件(资源)
print(file.closed) # True
# 自定义上下文管理器
class FileManager:def __init__(self, filename: str, mode: str) -> None:self.filename = filenameself.mode = modeself.file = None # 初始化文件对象def __enter__(self):print('>>> Open', self.filename)self.file = open(self.filename, self.mode)print(">>> file object:", self.file) # 测试return self.filedef __exit__(self, error_type, error_value, traceback):if self.file:print('>>> Close', self.filename)self.file.close()with FileManager('test.txt', 'r') as file:print(">>> file object:", file) # 测试print("==========<File Content>==========")print(file.read())print("==========<File Content>==========")# >>> Open test.txt
# >>> file object: <_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
# >>> file object: <_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
# ==========<File Content>==========
# This is a test file.
# ==========<File Content>==========
# >>> Close test.txt
class Sequence: # 顺序-sequencedef __init__(self, start: int, end: int) -> None:self.start = startself.end = enddef __enter__(self):self.data = list(range(self.start, self.end))return self.data# 参数:异常类型,异常值,异常回溯。 返回值:True(停止异常继续往外抛出)def __exit__(self, exc_type, exc_val, exc_tb):# 判断是否为索引越界的错误类型if isinstance(exc_val, IndexError): # isinstance(66, int) ==> 66 是不是 int 类的实例(对象),如果是,返回值True,如果不是,返回值Falseprint(f'索引超出范围:[{self.start}, {self.end})')return Truewith Sequence(1, 10) as seq:print(seq[5]) # 6print(seq[15]) # 索引超出范围:[1, 10)
import timeclass Timer:def __enter__(self):self.start = time.perf_counter()self.end = 0def elapsed():return self.end - self.startreturn elapseddef __exit__(self, exc_type, exc_val, exc_tb):self.end = time.perf_counter()with Timer() as timer:print('Start to do sth.')time.sleep(1)print('Stop to do sth.')print("运行时间:", timer())# Start to do sth.
# Stop to do sth.
# 运行时间: 1.0006713000002492