理想的类结构
@Property
装饰器
# 传统写法
class Square1:
def __init__(self):
self.__side = None
def get_side(self):
return self.__side
def set_side(self, side):
assert side >= 0, '边长不能为负数!'
self.__side = side
def del_side(self):
# del self.__side
self.__side = 0 # 这里并不打算真的去删除
# Pythonic写法
class Square2:
def __init__(self):
self.__side = None
@property
def side(self):
return self.__side
@side.setter
def side(self, side):
assert side >= 0, '边长不能为负数!'
self.__side = side
@side.deleter
def side(self):
# del self.__side
self.__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 可以在类内部使用,但不可以被继承
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def age(self):
return self.__age
@age.setter
def age(self, name):
self.__age = name
class Teacher(Person): # 继承Person类
@property
def 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):
pass
class B(object):
pass
class C(object):
pass
class D(object):
pass
class E(object):
pass
class K1(C, A, B):
pass
class K2(A, D):
pass
class K3(B, D, E):
pass
class Z(K1, K2, K3):
pass
print(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 = 0
F1 = 1
Fn = 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 = n
self.__previous = 0
self.__current = 1
self.__count = 0
def __iter__(self):
return self
def __next__(self):
if self.__count >= self.__n:
raise StopIteration
self.__count += 1
return_value = self.__previous
self.__previous, self.__current = self.__current, self.__previous + self.__current
return 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, timedelta
class DateRange:
def __init__(self, start_dt, end_dt):
self.start_dt = start_dt
self.end_dt = end_dt
self._range_values = self._get_range_values()
def _get_range_values(self):
data = []
current_dt = self.start_dt
while current_dt <= self.end_dt:
data.append(current_dt)
current_dt += timedelta(days=1)
return data
def __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 = name
self.price = price
self.num = num
class Promotion: # 促销方案
def __init__(self, lower_num: int, upper_num: int, rate: float) -> None:
self.__lower_num = lower_num
self.__upper_num = upper_num
self.__rate = rate
@property
def rate(self) -> float:
return self.__rate
def __contains__(self, product: Product) -> bool:
return self.__lower_num <= product.num <= self.__upper_num
def get_total_price(products: [Product], promotions: [Promotion]) -> float:
total_price = 0
for product in products:
promotion = [promotion for promotion in promotions if product in promotion][0] # in 触发 __contains__()方法
total_price += product.price * promotion.rate
return total_price
if __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 = name
def __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.
# Jack
person.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 self
my_print = MyPrint()
print(callable(my_print)) # True
my_print("你好呀")("我很好")("那就好")("都很好") # 你好呀我很好那就好都很好
__str__()
与__repr__()
class Person:
def __init__(self, name: str, hobby: [str]) -> None:
self.name = name
self.hobby = hobby
def __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 copy
list1 = [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 copy
class A:
def __repr__(self):
return f"A: {id(self)}"
class B:
def __init__(self, a: A) -> None:
self.a = a
self.b = self
self.c = 1
def __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 = 3
print(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):
@abstractmethod
def bind_data(self): # 抽象方法
pass
@abstractmethod
def 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 = name
self.age = age
def __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)
# False
from dataclasses import dataclass
@dataclass
class People:
name: str
age: int
obj1 = 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:
pass
obj = 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 time
def 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 time
def timer(func):
start = time.perf_counter() # 统计时间
res = func()
end = time.perf_counter()
print(f"耗时:{end - start}")
return res
def show():
print('showing...')
time.sleep(0.5)
return "OK"
result = timer(show)
print(result)
# showing...
# 耗时:0.5005822000093758
# OK
import time
def timer(func):
def inner(*args, **kwargs):
start = time.perf_counter() # 统计时间
res = func(*args, **kwargs)
end = time.perf_counter()
print(f"耗时:{end - start}")
return res
return inner
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.5001411000266671
# OK
# inner
import time
import functools
def 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 res
return inner
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.5001411000266671
# OK
# show
import time
import functools
def 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 res
return 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 functools
def 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 res
return inner
return 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_adult
return cls
@Entity() # Entity() 实例化一个对象. 对象 + __call__() 方法 == 函数()
class User:
def __init__(self, name, age):
self.name = age
self.age = age
# 本质:User = obj(User)
# 本质:User = cls
user = 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 = filename
self.mode = mode
self.file = None # 初始化文件对象
def __enter__(self):
print('>>> Open', self.filename)
self.file = open(self.filename, self.mode)
print(">>> file object:", self.file) # 测试
return self.file
def __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: # 顺序-sequence
def __init__(self, start: int, end: int) -> None:
self.start = start
self.end = end
def __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,如果不是,返回值False
print(f'索引超出范围:[{self.start}, {self.end})')
return True
with Sequence(1, 10) as seq:
print(seq[5]) # 6
print(seq[15]) # 索引超出范围:[1, 10)
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
self.end = 0
def elapsed():
return self.end - self.start
return elapsed
def __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