
|----------------|
| 你好,我是安然无虞。 |
文章目录
-
- 魔法方法
-
- [1. `new__和__del`](#1.
__new__和__del__
) - [2. `repr__和__len`](#2.
__repr__和__len__
) - [3. `enter__和__exit`](#3.
__enter__和__exit__
) - [4. 可迭代对象和迭代器](#4. 可迭代对象和迭代器)
- [5. 中括号`[]`数据操作](#5. 中括号
[]
数据操作) - [6. `getattr`、`setattr` 和 `delattr`](#6.
__getattr__
、__setattr__
和__delattr__
) - [7. 可调用的](#7. 可调用的)
- [8. 运算符](#8. 运算符)
- [1. `new__和__del`](#1.

魔法方法
魔法方法: Python中的魔法方法是一类特殊的方法, 它们以双下划线 __
开头和结尾, 用于实现类的特殊行为.这些魔法方法在Python中具有特殊的含义, 可以让你自定义类的行为, 使其更符合你的需求, 它们一般是自动调用, 也可以通过内置函数来显式调用.
1. __new__和__del__
__new__
是 Python 中的一个特殊魔法方法, 用于创建对象实例.它在对象实例被创建之前被调用, 通常用于控制对象的创建过程和自定义对象的创建方式.
与之相对的是 __init__
方法, 它在对象实例创建后进行初始化操作.
__new__
的注意点:
- 返回实例对象:
__new__
方法必须返回一个类的实例对象.通常情况下, 它会调用父类的__new__
方法来创建实例, 并返回实例对象. - 第一个参数是类:
__new__
方法的第一个参数是类本身, 通常命名为cls
.在调用时, Python 会自动传递类作为第一个参数. - 控制实例创建过程:
__new__
方法允许你控制实例的创建过程.你可以在这个方法中实现自定义的逻辑.
python
class MyClass:
# 在__init__之前被调用
def __new__(cls, name):
print("__new__")
obj = super().__new__(cls)
return obj
def __init__(self, name):
print("__init__")
self.name = name
my_class = MyClass('张三')
print(my_class.name)
# __new__
# __init__
# 张三
python
# __new__方法可以控制实例创建过程:
# 单例模式 - 无论你实例化多少次, 始终都是同一个对象
class Singleton:
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls)
return obj
def __init__(self):
pass
single1 = Singleton()
print(id(single1))
single2 = Singleton()
print(id(single2))
# 140613070163824
# 140613070163392
# 很明显上面并没有实现单例模式, 那怎么实现单例模式呢?
class Singleton:
__instance = None
def __new__(cls, *args, **kwargs):
# 假如已经实例化过了, 就直接返回已经存在的对象
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self):
pass
single1 = Singleton()
print(id(single1))
single2 = Singleton()
print(id(single2))
# 140240883584880
# 140240883584880
__del__
当对象被释放的时候, 会执行. 它通常用于执行一些资源释放或清理操作, 如关闭文件、释放网络连接等. 但是, 但并不保证一定会调用, 因为垃圾回收可能由不同的策略来管理. python中对象的释放是一个比较复杂的过程. 一个对象有可能在引用到0的时候被释放, 且这个释放是可能在任意一个地方发生.
注意:
__del__
跟python中的关键字del是没有关系的.del并不一定会触发__del__
python
class TestDel:
def __init__(self):
print("__init__")
def __del__(self):
print("__del__")
test_del = TestDel()
print()
Python当中的垃圾回收可以查看网上比较优秀的文章.
2. __repr__和__len__
__repr__
用于定义对象的字符串表示形式.
__str__
和__repr__
都返回一个对象的字符串描述, 不过两者的用途不同, str可以理解是给人阅读的, 而repr是给程序使用的.
print(obj)
、str(obj)
方法调用对象的str方法;交互式CLI、repr(obj)
、gdb调试时查看对象值返回的是repr, 不过在多情况下程序员把str和repr设置为一样__str__ == __repr__
.
如果没有定义 __str__
方法, 调用 print()
时会默认使用 __repr__
方法.
__len__
: 用于定义对象的长度(大小).当你在一个对象上调用内置的 len()
函数时, Python 会查找该对象是否定义了 __len__
方法, 如果有则调用该方法并返回长度值.
python
class MyList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
my_list = MyList('hello python')
print(len(my_list))
3. __enter__和__exit__
上下文协议是一组特殊方法(__enter__和__exit__同时存在), 允许你创建可用于 with
语句的上下文管理器.使用上下文管理器可以在进入和退出代码块时执行特定操作, 如资源的分配和释放.Python 的上下文协议涉及两个主要的特殊方法:__enter__
和 __exit__
.
python
__enter__(self)
__enter__
方法:
在进入with语句代码块前被调用, 用于执行一些准备操作.
- 可以返回一个值, 该值将被赋给
as
关键字之后的变量.
python
__exit__(self, exc_type, exc_value, exc_traceback)
__exit__
方法:
- 在退出with语句代码块时调用, 无论代码块是否发生异常.
- 可以用于执行一些清理操作, 如资源的释放.
- 如果代码块内没有异常发生, 参数为
None, None, None
.如果有异常, 参数包含异常类型、异常实例和跟踪信息. - 如果
__exit__
方法返回True
, 异常不会向上继续传播.返回False
则异常会继续向上抛.
上下文协议的使用案例:可以使用 with
语句来管理文件的打开和关闭, 确保文件资源在退出代码块时被正确释放.
python
class FileHandler:
# 一个实现了上下文协议的类
def __init__(self):
pass
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
pass
file_handler = FileHandler()
with file_handler:
print(11111111111)
python
logger = logging.getLogger(__name__)
class FileHandler:
# 一个实现了上下文协议的类
def __init__(self, file_name, mode, encoding='utf-8'):
self.file_name = file_name
self.mode = mode
self.encoding = encoding
self.file = None
def __enter__(self):
self.file = open(self.file_name, self.mode, encoding=self.encoding)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
logger.error(f"出错: {exc_val}")
if self.file:
print("文件正在关闭")
self.file.close()
return True
file_handler = FileHandler("test.py", "r")
with file_handler as f:
content = f.read()
print(content)
# 上下文协议的实现简化了异常捕获的代码, 让异常捕获的封装性更好
try:
file = open()
except Exception:
# 处理这个异常
pass
finally:
file.close()
4. 可迭代对象和迭代器
可迭代对象和迭代器是 Python 中用于遍历数据集合的概念.它们在很多情况下都用于实现数据的迭代操作, 如循环.
可迭代对象(Iterable)
可迭代对象是一种数据类型, 可以通过迭代获取其中的元素.
在 Python 中, 任何实现了 __iter__()
方法的对象都被视为可迭代对象.常见的可迭代对象包括列表、元组、字典、集合、字符串等.
可迭代对象的特点:
- 可以使用
for
循环遍历元素. - 可以通过
iter()
函数将其转换为迭代器.
python
class MyNumber:
def __init__(self, numbers):
self.numbers = numbers
def __iter__(self):
# __iter__()必须返回迭代器
return iter(self.numbers)
a_list = [1,2,3,4,5,6,7]
my_numbers = MyNumber(a_list)
print(isinstance(my_numbers, Iterable)) # True
for num in my_numbers:
print(num)
# 如果没有实现__iter__()就会报错:
class MyNumber:
def __init__(self, numbers):
self.numbers = numbers
a_list = [1,2,3,4,5,6,7]
my_numbers = MyNumber(a_list)
print(isinstance(my_numbers, Iterable)) False
for num in my_numbers:
print(num)
迭代器Iterator
迭代器是一个实现了 __iter__()
和 __next__()
方法的对象.__iter__()
方法返回迭代器对象自身, 而 __next__()
方法返回迭代器的下一个元素, 如果没有元素了, 抛出 StopIteration
异常.
迭代器的特点:
- 可以使用
for
循环遍历元素, 也可以使用next()
函数逐个获取元素. - 只能遍历一次, 遍历完成后就不能再次使用.
总结:
- 可迭代对象是具有迭代性质的对象, 而迭代器是一个实现了迭代协议的对象.
- 可迭代对象可以通过
iter()
函数转换为迭代器. - 迭代器是一种一次性的数据获取方式, 每次调用
next()
都会获取下一个元素.
python
class MyNumbersIterator:
def __init__(self):
pass
def __iter__(self):
pass
def __next__(self):
pass
my_numbers_iterator = MyNumbersIterator()
print(isinstance(my_numbers_iterator, Iterable)) # True
print(isinstance(my_numbers_iterator, Iterator)) # True
# ----------------------------------------------------------------
class MyNumbersIterator:
def __init__(self, numbers):
self.numbers = numbers
self.index = 0
def __iter__(self):
# __iter__()必须返回迭代器
return self
def __next__(self):
try:
num = self.numbers[self.index]
self.index += 1
except IndexError:
raise StopIteration
return num
a_list = [1,2,3,4,5,6,7]
my_numbers_iterator = MyNumbersIterator(a_list)
print(isinstance(my_numbers_iterator, Iterable))
print(isinstance(my_numbers_iterator, Iterator))
5. 中括号[]
数据操作
__getitem__
、__setitem__
和 __delitem__
用于自定义对象的索引访问和操作.它们在创建自定义的容器类时非常有用, 允许你实现类似字典或列表的行为.
-
__getitem__(self, key)
:这个方法定义了使用索引访问对象时的行为.当你像
obj[key]
这样使用索引操作时, Python 会调用对象的__getitem__
方法, 并将索引key
作为参数传递给这个方法.你可以在这个方法中实现对应的行为, 比如从内部数据结构中获取相应的值. -
__setitem__(self, key, value)
:这个方法定义了使用索引赋值操作时的行为.当你像
obj[key] = value
这样进行索引赋值操作时, Python 会调用对象的__setitem__
方法, 并将索引key
和值value
作为参数传递给这个方法.你可以在这个方法中实现对应的赋值行为. -
__delitem__(self, key)
:这个方法定义了使用
del obj[key]
进行索引删除操作时的行为.当你使用del
删除索引时, Python 会调用对象的__delitem__
方法, 并将索引key
作为参数传递给这个方法.你可以在这个方法中实现删除操作的逻辑.
这些方法的使用可以使你的自定义类更具像内置容器类型(如字典和列表)的行为, 从而实现更灵活和符合预期的数据操作.
python
# 补充知识:
# 列表切片 返回的是 列表.
# 元组切片 返回是是 元组.
# print(my_numbers[1:3]) # 切片my_numbers[1:3]相当于slice(1,3,None)
# s = slice(1, 3, None)
from collections.abc import Iterable
class MyNumbers:
# 全部都是数字
def __init__(self, numbers):
self.numbers = numbers
def __getitem__(self, item):
if isinstance(item, slice):
# 如果进了判断, 就代表是在做切片, 则需要返回与原本数据类型一致的数据类型.
temp = self.numbers[item]
cls = type(self)
obj = cls(temp)
return obj
else:
num = self.numbers[item]
return num
def __setitem__(self, key, value):
if isinstance(value, (int, float)):
self.numbers[key] = value
else:
raise ValueError(f'{value}的值设置必须是数字类型.')
def __str__(self):
return f"{self.numbers}"
def __len__(self):
return len(self.numbers)
def __contains__(self, item):
return item in self.numbers
def __delitem__(self, key):
# 不允许为空, 起码要有一个元素.
if len(self.numbers) > 1:
self.numbers.pop(key)
else:
raise ValueError('序列起码要有一个元素')
a_list = [1, 2, 3, 4, 5, 6]
# a_dict = {'a': 1, 'b': 2, 'c': 3}
my_numbers = MyNumbers(a_list)
# my_numbers[0] = 8
del my_numbers[0]
del my_numbers[1]
del my_numbers[2]
del my_numbers[0]
del my_numbers[0]
del my_numbers[0]
print(my_numbers)
# print(2 in my_numbers)
# print(type(my_numbers))
# s = slice(1, 3, None)
# print(type(a_list[s]))
# 列表切片 返回的是 列表.
# 元组切片 返回是是 元组.
# print(my_numbers[1:3])
# print(isinstance(my_numbers, Iterable))
#
# for num in my_numbers:
# print(num)
6. __getattr__
、__setattr__
和 __delattr__
__getattr__
、__setattr__
和 __delattr__
是 Python 中的魔法方法, 用于自定义对象的属性访问和操作.它们允许你控制属性的获取、设置和删除行为, 从而实现自定义的属性操作逻辑.
-
__getattr__(self, name)
:当你访问一个不存在的属性时, Python 会调用对象的
__getattr__
方法, 并将属性名name
作为参数传递给这个方法.你可以在这个方法中实现对应的行为, 比如返回一个默认值或者抛出异常. -
__setattr__(self, name, value)
:当你设置属性的值时, Python 会调用对象的
__setattr__
方法, 并将属性名name
和值value
作为参数传递给这个方法.你可以在这个方法中实现对应的赋值行为, 比如进行值的验证或修改. -
__delattr__(self, name)
:当你删除属性时, Python 会调用对象的
__delattr__
方法, 并将属性名name
作为参数传递给这个方法.你可以在这个方法中实现删除属性的逻辑.
这些方法的使用可以使你的自定义类的属性操作更具控制性和灵活性.
python
class Person:
def __init__(self, name, age, info):
self.name = name
self.age = age
self.info = info
def __getattr__(self, item):
if item in self.info:
return self.info[item]
else:
raise AttributeError(f'{self.__class__}没有{item}属性')
def __setattr__(self, key, value):
# print(f'__setattr__接收到的参数key的值是{key}, value的值是{value}')
if key == 'age':
if 0 < value < 150:
super().__setattr__(key, value)
else:
raise AttributeError(f"{key}的值{value}设置不合法")
super().__setattr__(key, value)
def __delattr__(self, item):
if item in ['name', 'age']:
raise AttributeError(f"{item!r}是基础属性, 不允许删除")
super(Person, self).__delattr__(item)
information = {
'gender': "男",
'height': 180,
'hobby': "打篮球",
}
person = Person('张三', 20, information)
person.age = 25
del person.age
print(person.name)
print(person.age)
print(person.gender)
# print(person.height)
# print(person.hobby)
# print(person.aaa)
7. 可调用的
不仅 Python 函数是真正的可调用的, 任何 Python 对象都可以表现得像函数.为此, 只需实现魔法方法 call.
__call__
是 Python 中的一个特殊魔法方法, 用于使对象可以像函数一样被调用.当一个对象被调用时, Python 会检查对象是否定义了 __call__
方法, 如果定义了, 就会调用该方法, 从而实现对象的可调用行为.
这使得你可以将一个类的实例像函数一样使用, 提供更多的灵活性和自定义操作.
python
from collections.abc import Callable
class Add:
def __call__(self, a, b):
return a + b
# def func():
# print(111)
# add = Add()
# res = add(1, 2)
# print(res)
# print(isinstance(func, Callable))
# print(isinstance(test_call, Callable))
# 类装饰器.
# 创建一个装饰器函数,使被装饰的函数只能在特定的时间段内执行。
# 在早上9点 -> 晚上6点之间, 可以调用, 其余时间不允许调用.
import datetime
def time_limited(start, end):
def outer(func):
def inner(*args, **kwargs):
now = datetime.datetime.now().time()
if start < now < end:
result = func(*args, **kwargs)
return result
else:
print(f"对不起, 该时间段无法调用此接口.")
return inner
return outer
class TimeLimit:
def __init__(self, start, end):
self.start = start
self.end = end
def __call__(self, f):
def inner(*args, **kwargs):
now = datetime.datetime.now().time()
if self.start < now < self.end:
result = f(*args, **kwargs)
return result
else:
print(f"对不起, 该时间段无法调用此接口.")
return inner
# @time_limited(start=datetime.time(9, 0), end=datetime.time(18, 0))
@TimeLimit(start=datetime.time(9, 0), end=datetime.time(18, 0))
def ceshi():
print('我只能在特定的时间段内执行.')
if __name__ == '__main__':
ceshi()
8. 运算符
运算符魔法方法是用于实现对象之间的比较和关系运算的.它们允许你定义自定义的比较操作, 使你的对象可以像内置类型一样进行大小比较、相等性检查等操作.
下面是一些常用的运算符类型的魔法方法及其解释:
__lt__(self, other)
: 小于运算符<
的魔法方法.定义对象小于另一个对象时的行为.__le__(self, other)
: 小于等于运算符<=
的魔法方法.定义对象小于等于另一个对象时的行为.__eq__(self, other)
: 等于运算符==
的魔法方法.定义对象相等时的行为.__ne__(self, other)
: 不等于运算符!=
的魔法方法.定义对象不等于另一个对象时的行为.__gt__(self, other)
: 大于运算符>
的魔法方法.定义对象大于另一个对象时的行为.__ge__(self, other)
: 大于等于运算符>=
的魔法方法.定义对象大于等于另一个对象时的行为.
python
import random
class IntNumber:
def __init__(self):
self.value = random.randint(1, 10000)
def __str__(self):
return f"{self.value}"
def __gt__(self, other):
return self.value > other.value
def __eq__(self, other):
return self.value == other.value
def __add__(self, other):
return self.value + other.value
class FloatNumber:
def __init__(self):
self.value = random.uniform(1, 10000)
def __str__(self):
return f"{self.value}"
def __lt__(self, other):
return self.value < other.value
int_number = IntNumber()
int_number2 = IntNumber()
print(int_number + int_number2)
# float_number = FloatNumber()
# print(int_number)
# print(int_number2)
# print(float_number)
# print(int_number2 != int_number)
# print(int_number is float_number)
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __gt__(self, other):
if self.year > other.year:
return True
if self.year == other.year:
if self.month > other.month:
return True
elif self.month == other.month:
return self.day > other.day
else:
return False
if self.year < other.year:
return False
def __eq__(self, other):
return self.year == other.year and self.month == other.month and self.day == other.day
# date1 = Date(2022, 7, 15)
# date2 = Date(2022, 7, 15)
#
# print(date1 == date2)
|----------------------|
| 遇见安然遇见你,不负代码不负卿。 |
| 谢谢老铁的时间,咱们下篇再见~ |