|
文章目錄
- 魔法方法
- 1. `__new__和__del__`
- 2. `__repr__和__len__`
- 3. `__enter__和__exit__`
- 4. 可迭代對象和迭代器
- 5. 中括號`[]`數據操作
- 6. `__getattr__`、`__setattr__` 和 `__delattr__`
- 7. 可調用的
- 8. 運算符

魔法方法
魔法方法: Python中的魔法方法是一類特殊的方法, 它們以雙下劃線 __
開頭和結尾, 用于實現類的特殊行為.這些魔法方法在Python中具有特殊的含義, 可以讓你自定義類的行為, 使其更符合你的需求, 它們一般是自動調用, 也可以通過內置函數來顯式調用.
1. __new__和__del__
__new__
是 Python 中的一個特殊魔法方法, 用于創建對象實例.它在對象實例被創建之前被調用, 通常用于控制對象的創建過程和自定義對象的創建方式.
與之相對的是 __init__
方法, 它在對象實例創建后進行初始化操作.
__new__
的注意點:
- 返回實例對象:
__new__
方法必須返回一個類的實例對象.通常情況下, 它會調用父類的__new__
方法來創建實例, 并返回實例對象. - 第一個參數是類:
__new__
方法的第一個參數是類本身, 通常命名為cls
.在調用時, Python 會自動傳遞類作為第一個參數. - 控制實例創建過程:
__new__
方法允許你控制實例的創建過程.你可以在這個方法中實現自定義的邏輯.
class MyClass:# 在__init__之前被調用def __new__(cls, name):print("__new__")obj = super().__new__(cls)return objdef __init__(self, name):print("__init__")self.name = namemy_class = MyClass('張三')
print(my_class.name)# __new__
# __init__
# 張三
# __new__方法可以控制實例創建過程:
# 單例模式 - 無論你實例化多少次, 始終都是同一個對象class Singleton:def __new__(cls, *args, **kwargs):obj = super().__new__(cls)return objdef __init__(self):passsingle1 = Singleton()
print(id(single1))
single2 = Singleton()
print(id(single2))# 140613070163824
# 140613070163392# 很明顯上面并沒有實現單例模式, 那怎么實現單例模式呢?
class Singleton:__instance = Nonedef __new__(cls, *args, **kwargs):# 假如已經實例化過了, 就直接返回已經存在的對象if not cls.__instance:cls.__instance = super().__new__(cls)return cls.__instancedef __init__(self):passsingle1 = Singleton()
print(id(single1))
single2 = Singleton()
print(id(single2))# 140240883584880
# 140240883584880
__del__
當對象被釋放的時候, 會執行. 它通常用于執行一些資源釋放或清理操作, 如關閉文件、釋放網絡連接等. 但是, 但并不保證一定會調用, 因為垃圾回收可能由不同的策略來管理. python中對象的釋放是一個比較復雜的過程. 一個對象有可能在引用到0的時候被釋放, 且這個釋放是可能在任意一個地方發生.
注意:
__del__
跟python中的關鍵字del是沒有關系的.del并不一定會觸發__del__
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__
方法, 如果有則調用該方法并返回長度值.
class MyList:def __init__(self, items):self.items = itemsdef __len__(self):return len(self.items)my_list = MyList('hello python')
print(len(my_list))
3. __enter__和__exit__
上下文協議是一組特殊方法(__enter__和__exit__同時存在), 允許你創建可用于 with
語句的上下文管理器.使用上下文管理器可以在進入和退出代碼塊時執行特定操作, 如資源的分配和釋放.Python 的上下文協議涉及兩個主要的特殊方法:__enter__
和 __exit__
.
__enter__(self)
__enter__
方法:
在進入with語句代碼塊前被調用, 用于執行一些準備操作.
- 可以返回一個值, 該值將被賦給
as
關鍵字之后的變量.
__exit__(self, exc_type, exc_value, exc_traceback)
__exit__
方法:
- 在退出with語句代碼塊時調用, 無論代碼塊是否發生異常.
- 可以用于執行一些清理操作, 如資源的釋放.
- 如果代碼塊內沒有異常發生, 參數為
None, None, None
.如果有異常, 參數包含異常類型、異常實例和跟蹤信息. - 如果
__exit__
方法返回True
, 異常不會向上繼續傳播.返回False
則異常會繼續向上拋.
上下文協議的使用案例:可以使用 with
語句來管理文件的打開和關閉, 確保文件資源在退出代碼塊時被正確釋放.
class FileHandler:# 一個實現了上下文協議的類def __init__(self):passdef __enter__(self):passdef __exit__(self, exc_type, exc_val, exc_tb):passfile_handler = FileHandler()
with file_handler:print(11111111111)
logger = logging.getLogger(__name__)class FileHandler:# 一個實現了上下文協議的類def __init__(self, file_name, mode, encoding='utf-8'):self.file_name = file_nameself.mode = modeself.encoding = encodingself.file = Nonedef __enter__(self):self.file = open(self.file_name, self.mode, encoding=self.encoding)return self.filedef __exit__(self, exc_type, exc_val, exc_tb):if exc_type:logger.error(f"出錯: {exc_val}")if self.file:print("文件正在關閉")self.file.close()return Truefile_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()
函數將其轉換為迭代器.
class MyNumber:def __init__(self, numbers):self.numbers = numbersdef __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)) # Truefor num in my_numbers:print(num)# 如果沒有實現__iter__()就會報錯:
class MyNumber:def __init__(self, numbers):self.numbers = numbersa_list = [1,2,3,4,5,6,7]
my_numbers = MyNumber(a_list)print(isinstance(my_numbers, Iterable)) Falsefor num in my_numbers:print(num)
迭代器Iterator
迭代器是一個實現了 __iter__()
和 __next__()
方法的對象.__iter__()
方法返回迭代器對象自身, 而 __next__()
方法返回迭代器的下一個元素, 如果沒有元素了, 拋出 StopIteration
異常.
迭代器的特點:
- 可以使用
for
循環遍歷元素, 也可以使用next()
函數逐個獲取元素. - 只能遍歷一次, 遍歷完成后就不能再次使用.
總結:
- 可迭代對象是具有迭代性質的對象, 而迭代器是一個實現了迭代協議的對象.
- 可迭代對象可以通過
iter()
函數轉換為迭代器. - 迭代器是一種一次性的數據獲取方式, 每次調用
next()
都會獲取下一個元素.
class MyNumbersIterator:def __init__(self):passdef __iter__(self):passdef __next__(self):passmy_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 = numbersself.index = 0def __iter__(self):# __iter__()必須返回迭代器return selfdef __next__(self):try:num = self.numbers[self.index]self.index += 1except IndexError:raise StopIterationreturn numa_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
作為參數傳遞給這個方法.你可以在這個方法中實現刪除操作的邏輯.
這些方法的使用可以使你的自定義類更具像內置容器類型(如字典和列表)的行為, 從而實現更靈活和符合預期的數據操作.
# 補充知識:
# 列表切片 返回的是 列表.
# 元組切片 返回是是 元組.
# print(my_numbers[1:3]) # 切片my_numbers[1:3]相當于slice(1,3,None)
# s = slice(1, 3, None)from collections.abc import Iterableclass MyNumbers:# 全部都是數字def __init__(self, numbers):self.numbers = numbersdef __getitem__(self, item):if isinstance(item, slice):# 如果進了判斷, 就代表是在做切片, 則需要返回與原本數據類型一致的數據類型.temp = self.numbers[item]cls = type(self)obj = cls(temp)return objelse:num = self.numbers[item]return numdef __setitem__(self, key, value):if isinstance(value, (int, float)):self.numbers[key] = valueelse: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.numbersdef __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
作為參數傳遞給這個方法.你可以在這個方法中實現刪除屬性的邏輯.
這些方法的使用可以使你的自定義類的屬性操作更具控制性和靈活性.
class Person:def __init__(self, name, age, info):self.name = nameself.age = ageself.info = infodef __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__
方法, 如果定義了, 就會調用該方法, 從而實現對象的可調用行為.
這使得你可以將一個類的實例像函數一樣使用, 提供更多的靈活性和自定義操作.
from collections.abc import Callableclass 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 datetimedef 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 resultelse:print(f"對不起, 該時間段無法調用此接口.")return innerreturn outerclass TimeLimit:def __init__(self, start, end):self.start = startself.end = enddef __call__(self, f):def inner(*args, **kwargs):now = datetime.datetime.now().time()if self.start < now < self.end:result = f(*args, **kwargs)return resultelse: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)
: 大于等于運算符>=
的魔法方法.定義對象大于等于另一個對象時的行為.
import randomclass 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.valuedef __eq__(self, other):return self.value == other.valuedef __add__(self, other):return self.value + other.valueclass 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.valueint_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 = yearself.month = monthself.day = daydef __gt__(self, other):if self.year > other.year:return Trueif self.year == other.year:if self.month > other.month:return Trueelif self.month == other.month:return self.day > other.dayelse:return Falseif self.year < other.year:return Falsedef __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)
|
|