【3.0】基础串联之魔法方法
【一】__init__
方法
类实例化会触发
__init__
是Python中的一个特殊方法,用于在创建一个对象时进行初始化操作。- 它是在类实例化(创建对象)时自动调用的。
__init__
方法的作用是对新创建的对象进行初始化操作,可以在这个方法中设置对象的初始状态、定义属性等。- 该方法的定义格式如下:
def __init__(self, 参数列表):
# 初始化操作
- 其中
self
表示当前对象的引用self
参数是在定义类方法时必须放在第一个位置的。- 它代表了类实例本身
- 在方法内部可以使用
self
来访问对象的属性和方法。
__init__
方法常用于执行以下操作:
- 初始化对象的属性:在方法内部可以使用
self.属性名
来定义和初始化对象的属性。 - 执行其他必要的初始化操作:例如打开文件、建立数据库连接等。
示例代码如下所示:
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age
obj = MyClass("Alice", 20)
print(obj.name) # 输出 "Alice"
print(obj.age) # 输出 20
- 在示例中
- 我们定义了一个
MyClass
类 - 其中的
__init__
方法接收两个参数name
和age
- 并将它们分别赋值给对象的属性
name
和age
。
- 并将它们分别赋值给对象的属性
- 然后通过实例化类
- 并访问对象的属性
- 可以得到正确的结果。
- 我们定义了一个
- 通过定义
__init__
方法- 我们可以在创建对象时进行初始化操作
- 为对象提供必要的属性和初始状态
- 使其更易于使用和维护。
【二】__str__
方法
打印对象会触发
__str__
是Python中的一个特殊方法,用于返回对象的字符串表示。- 当我们在打印对象或使用
str()
函数时 - Python会自动调用该方法并将其返回值作为字符串输出。
- 当我们在打印对象或使用
__str__
方法的作用是定义对象的字符串表示,通常用于提供对象的可读性和描述性信息。- 我们可以在该方法中返回一个描述对象的字符串
- 包括对象的属性值、状态等。
__str__
方法的定义格式如下:
def __str__(self):
# 返回对象的字符串表示
- 其中,
self
表示当前对象的引用。- 在方法内部,可以使用
self
来访问对象的属性和方法。
- 在方法内部,可以使用
示例代码如下所示:
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Name: {self.name}, Age: {self.age}"
obj = MyClass("Alice", 20)
print(obj) # 输出 "Name: Alice, Age: 20"
- 在示例中
- 我们给
MyClass
类添加了一个__str__
方法。 - 该方法返回了一个描述对象的字符串,包括对象的姓名和年龄。
- 通过在打印对象时自动调用
__str__
方法,我们可以获得对象的可读性较好的字符串表示。
- 我们给
- 注意:
__str__
方法应该返回一个字符串类型的值,并且不应该有任何副作用。- 如果需要有副作用的操作(例如改变对象状态),应该使用
__repr__
方法来定义对象的字符串表示。
【三】__call__
方法
- 对象()会触发
- 类也是对象
- 类()
- 类的实例化过程调用元类的
__call__
方法
__call__
方法是Python中的一个特殊方法,用于使对象具有像函数一样的调用行为。- 通过在类中实现
__call__
方法,我们可以直接对对象进行调用,就像调用函数一样。
- 通过在类中实现
- 当我们对一个对象使用括号进行调用时,Python会自动调用该对象的
__call__
方法,并将传入的参数作为__call__
方法的参数。
__call__
方法的定义格式如下:
def __call__(self, *args, **kwargs):
# 对象的调用行为
- 其中
self
表示当前对象的引用args
和kwargs
分别表示传入的位置参数和关键字参数。
示例代码如下所示:
class MyCallable:
def __init__(self):
pass
def __call__(self, x, y):
return x + y
obj = MyCallable()
result = obj(3, 5)
print(result) # 输出 8
- 在示例中
- 我们给
MyCallable
类添加了一个__call__
方法。 - 该方法实现了对对象的调用行为,即将传入的两个参数相加并返回结果。
- 通过对
obj
对象进行调用,就可以像函数一样计算两个数的和。
- 我们给
- 需要注意的是
- 通过实现
__call__
方法,我们可以将一个类的实例对象当作函数来使用。- 这种方式可以使得类的实例对象具有可调用的行为,从而增加了代码的灵活性。
【四】__new__
方法
- 在类实例化会触发,它比
__init__
早
__new__
造出裸体的人__init__
给裸体的人穿衣服
__new__
方法是Python中的一个特殊方法,用于创建对象实例。- 与
__init__
方法不同,__new__
方法在对象实例化之前被调用,用于创建并返回实例对象。
- 与
__new__
方法的定义格式如下:
def __new__(cls, *args, **kwargs):
# 创建并返回实例对象
-
其中
cls
表示当前类的引用args
和kwargs
分别表示传入的位置参数和关键字参数。
-
__new__
方法需要返回创建的实例对象。- 这个返回值会作为参数传递给
__init__
方法 - 并成为
self
参数的值。
- 这个返回值会作为参数传递给
示例代码如下所示:
class MyClass:
def __new__(cls, *args, **kwargs):
print("__new__ is called")
instance = object.__new__(cls)
return instance
def __init__(self, x, y):
print("__init__ is called")
self.x = x
self.y = y
obj = MyClass(3, 5)
- 在示例中
- 我们给
MyClass
类添加了一个__new__
方法。 - 该方法通过调用
object.__new__(cls)
来创建并返回实例对象。 - 然后,创建得到的实例对象将作为参数传递给
__init__
方法。
- 我们给
输出结果如下所示:
__new__ is called
__init__ is called
- 从输出结果可以看出
__new__
方法首先被调用,用于创建实例对象。- 然后,
__init__
方法被调用,用于对实例进行初始化。
- 需要注意的是
- 通常情况下不需要重写
__new__
方法,因为Python中的对象创建过程已经由内部的object.__new__
方法完成了。- 只有在特殊情况下需要对对象的创建过程进行定制化时,才需要重写
__new__
方法。
- 综上所述
__new__
方法在对象实例化之前被调用,并用于创建并返回实例对象。- 通过重写
__new__
方法,我们可以对对象的创建过程进行定制化。
【五】__del__
方法
对象,对象回收的时候触发
__del__
方法是Python中的一个特殊方法,用于在对象被销毁时进行清理操作。- 当一个对象不再被使用时,Python的垃圾回收机制会自动销毁该对象。
- 在对象销毁之前,如果定义了
__del__
方法,它会被调用。
【1】__del__
方法的定义:
- 在一个类中,可以定义
__del__
方法来实现对象销毁前的清理工作。 - 该方法的命名规则是以双下划线开始和结束,例如
__del__()
。
【2】垃圾回收机制与__del__
方法的关系:
- Python采用了自动垃圾回收机制来管理内存,当一个对象不再被引用时,垃圾回收机制会自动销毁该对象。
- 在销毁对象之前,会调用对象的
__del__
方法进行清理操作。
【3】__del__
方法的执行时机:
__del__
方法的执行时机是在对象将要被销毁时(即引用计数减为0),而不是在对象被销毁之后。- 因此,在
__del__
方法中不能再访问这个对象的任何属性或方法,否则会引发异常。
【4】__del__
方法的注意事项:
- 不应该主动调用
__del__
方法,它由Python的垃圾回收机制自动触发。 - 在
__del__
方法中不应该做过多的操作,以免影响程序性能。 - 运行时环境和垃圾回收机制的具体实现方式可能会影响
__del__
方法的执行时间和顺序,因此不应该过于依赖该方法进行程序逻辑的设计。
【5】使用场景:
__del__
方法可以用于执行一些资源释放的操作,比如关闭文件、释放网络连接、释放数据库连接等。这样可以确保在对象被销毁之前,相关的资源得到正确释放,避免资源泄漏或异常情况。
【6】综上所述
__del__
方法是Python提供的用于在对象销毁前进行清理操作的特殊方法。- 合理地使用
__del__
方法可以确保资源的正确释放,但需要注意不要滥用此方法并了解其执行时机与局限性。
【六】__setattr__
/__getattr__
方法
- 拦截方法
- 当对象.属性
- 赋值会触发
__setattr__
- 如果取值会触发
__getattr__
__setattr__
和__getattr__
是Python中的两个特殊方法,用于处理属性的设置和获取操作。- 它们提供了灵活的方式来自定义对象属性的行为。
【1】__setattr__
方法:
__setattr__
方法在给对象设置属性时被调用。- 当属性被设置时,Python会自动寻找并调用该方法。
- 通过在类中定义
__setattr__
方法,我们可以自定义属性设置的逻辑,例如检查属性值的有效性、记录属性变化等。
【2】__getattr__
方法:
__getattr__
方法在访问不存在或无法访问,就会被调用。__getattr__
方法的返回值将作为属性访问的结果。
【3】__setattr__
和__getattr__
的注意事项:
- 定义这两个方法时,需要注意避免无限递归调用。
- 因为在这两个方法中访问同一个属性可能会再次触发方法本身的调用。
- 为避免递归调用,在
__setattr__
方法中,可以使用super().__setattr__(name, value)
来调用基类的__setattr__
方法,而不是直接赋值。 - 在
__getattr__
方法中,如果返回的是AttributeError
异常,Python会抛出AttributeError
错误。- 如果要实现默认属性值或动态生成属性的功能,可以在方法中进行处理。
【4】使用场景:
__setattr__
方法可以用于对属性的赋值进行控制和约束- 例如检查属性值的类型、范围或有效性。
__getattr__
方法可以用于动态生成属性,或在属性不存在时返回默认值或执行特定的逻辑。
下面是一个简单的示例代码:
class MyClass:
def __setattr__(self, name, value):
# 自定义属性设置逻辑
print(f"Setting attribute {name} to {value}")
# 调用基类的__setattr__方法
super().__setattr__(name, value)
def __getattr__(self, name):
# 自定义属性获取逻辑
print(f"Attribute {name} does not exist")
return None
【5】综上所述
__setattr__
和__getattr__
方法是Python中的两个特殊方法,用于自定义对象属性的设置和获取逻辑。- 通过合理使用这两个方法,可以灵活地控制属性的行为和处理方式。
【七】__getitem__
/__setitem__
方法
class Person: def __init__(self, name): self.name = name def __setitem__(self, key, value): # 反射方法设置值 setattr(self, key, value) def __getitem__(self, item): # 反射方法取值 return getattr(self, item) p = Person("dream") p.name = "aaa" # 正常情况下,该方法会报错,但是可以通过重写 __setitem__ 方法完成操作 p["name"] = 10 print(p.name) # 10 # 正常情况下,该方法会报错,但是可以通过重写 __getitem__ 方法完成操作 print(p["name"]) # 10
__getitem__
和__setitem__
方法是Python中的特殊方法- 用于实现对象的索引操作和赋值操作。
【1】__getitem__
方法:
__getitem__
方法在使用索引访问对象时被调用。- 当对象通过索引访问时,Python会自动寻找并调用该方法。
__getitem__
方法接收一个参数,即要获取的索引值。- 返回值将作为索引访问的结果。
【2】__setitem__
方法:
__setitem__
方法在对对象进行索引赋值时被调用。- 当对象通过索引赋值时,Python会自动寻找并调用该方法。
__setitem__
方法接收两个参数,第一个参数为要设置的索引值,第二个参数为要赋的值。
【3】使用场景:
__getitem__
和__setitem__
方法常用于自定义序列类型(例如列表、字典)等。- 可以根据索引值进行不同的处理,如获取指定位置的元素、切片操作、赋值等。
【4】示例代码:
class MyList:
def __init__(self, *args):
self.data = list(args)
def __getitem__(self, index):
# 自定义索引访问逻辑
return self.data[index]
def __setitem__(self, index, value):
# 自定义索引赋值逻辑
self.data[index] = value
my_list = MyList(1, 2, 3, 4, 5)
print(my_list[0]) # 输出:1
print(my_list[2]) # 输出:3
print(my_list[1:3]) # 输出:[2, 3]
my_list[0] = 10 # 索引赋值
print(my_list[0]) # 输出:10
- 在示例代码中
MyList
类定义了__getitem__
和__setitem__
方法- 使得可以通过索引访问
data
属性中的元素,并且可以通过索引进行值的赋值。
【5】综上所述
__getitem__
和__setitem__
方法是Python中的特殊方法,在对象索引访问和赋值时被调用。- 通过这两个方法,我们可以自定义对象的索引操作和赋值操作,从而实现对对象内容的灵活访问和修改。
【八】__repr__
方法
在交互时环境下打印对象
-
__repr__
方法是Python中的一个特殊方法,用于返回对象的字符串表示形式。- 它被称为"official string representation",即官方字符串表示形式。
- 在执行
repr(obj)
时,实际上是调用了obj.__repr__()
方法来获取对象的字符串表示。
-
__repr__
方法的目的是为了提供一个明确、准确且可读性较高的字符串表示形式,使开发者能够快速了解对象的类型和状态。 -
一般情况下,
__repr__
方法的返回值应该是可以通过eval()
函数重新创建相同对象的字符串。
示例:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(2, 3)
print(p) # 输出:Point(2, 3)
- 在上面的例子中,
Point
类实现了__repr__
方法,它返回了一个以Point(x, y)
形式表示的字符串。 - 通过定义了
__repr__
方法,我们可以直接打印Point
对象,并获得它的字符串表示形式,从而方便地了解对象的内容。
- 需要注意的是,
__repr__
方法通常应该返回一个能够被解释器接受的字符串,而不是一个人类友好的描述。- 如果想要提供一个人类友好的描述,应该使用
__str__
方法。
- 总结:
__repr__
方法是一个特殊方法,用于返回对象的官方字符串表示形式。- 它的目的是为了提供一个明确、准确且可读性较高的字符串表示,方便开发者了解对象的类型和状态。
【九】__enter__
/__exit__
方法
上下文管理器
-
__enter__
和__exit__
是Python中用于上下文管理的特殊方法。 -
上下文管理器可以通过with语句来管理资源的获取和释放
- 例如文件的打开和关闭、锁的获取和释放等。
-
__enter__
方法定义了进入上下文管理器时的行为,它在进入with代码块之前被调用,并且该方法可以返回一个值,供with语句中的as子句使用。 -
一般情况下,
__enter__
方法用于获取资源,并返回该资源或者一些用于操作资源的对象。 -
__exit__
方法定义了离开上下文管理器时的行为,它在退出with代码块之后被调用。 -
__exit__
方法接收三个参数- 分别是异常类型
- 异常值
- 追溯信息。
-
如果with代码块内部发生了异常,则这些异常信息会传递给
__exit__
方法。 -
__exit__
方法通常用于释放资源,关闭文件、释放锁以及处理异常等操作。
下面是一个使用上下文管理器的示例:
class FileHandler:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
# 在 with 管理的时候,会触发
# 进入with 语句块时执行此方法,此方法如果有返回值就会赋值给as声明的变量
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_value, traceback):
# 退出 with 代码时,执行此方法
self.file.close()
# 使用上下文管理器写入文件
with FileHandler('example.txt') as file:
file.write('Hello, World!')
-
在上面的例子中
- 我们定义了一个
FileHandler
类作为上下文管理器。 - 在
__enter__
方法中- 我们打开文件并返回该文件对象。
- 在
__exit__
方法中- 我们关闭文件。
- 通过使用with语句,并将上下文管理器赋值给变量
file
,我们可以在代码块内部使用这个文件对象进行操作。 - 在with代码块结束后,
__exit__
方法会被调用,确保文件被关闭。
- 我们定义了一个
-
总结:
__enter__
和__exit__
是Python中用于上下文管理的特殊方法。__enter__
方法在进入with代码块之前被调用,用于获取资源并返回对象。__exit__
方法在退出with代码块之后被调用,用于释放资源、处理异常等操作。- 通过使用上下文管理器,可以更方便地管理资源的获取和释放。
【十】__eq__
方法
__eq__
方法是一个魔法方法- 用于定义对象的相等性比较操作。
- 它在对象之间进行相等性比较时被调用。
【1】功能:
__eq__
方法用于比较两个对象是否相等。- 它定义了对象相等的条件和逻辑,并返回一个布尔值,表示对象是否相等。
【2】用法:
- 在自定义的类中,可以通过重写
__eq__
方法来定义对象的相等性比较操作。 - 通常,我们希望相等的对象具有相同的属性值,因此在
__eq__
方法中,我们可以使用对象的属性进行比较。
【3】示例
class MyClass:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, MyClass):
return self.value == other.value
return False
# 使用示例
obj1 = MyClass(10)
obj2 = MyClass(20)
obj3 = MyClass(10)
print(obj1 == obj2) # 输出 False
print(obj1 == obj3) # 输出 True
- 在上述示例代码中
- 我们定义了一个
MyClass
类,该类具有一个属性value
。 - 我们重写了
__eq__
方法,使其在比较对象相等性时,以对象的属性值作为判断依据。
- 我们定义了一个
- 在使用示例中
- 我们创建了三个
MyClass
对象obj1
、obj2
和obj3
,它们具有不同的属性值。 - 通过使用
==
运算符进行比较- 我们可以看到
obj1
与obj2
的属性值不相等,返回 False - 而
obj1
与obj3
的属性值相等,返回 True。
- 我们可以看到
- 我们创建了三个
- 需要注意的是
- 当我们重写
__eq__
方法时,通常也应同时重写__ne__
方法,以确保对象的不等性比较也能正确工作。__ne__
方法用于定义对象的不等性比较操作。在上述示例中,未实现__ne__
方法,因此会默认使用__eq__
方法与not
运算符一起来实现不等性比较。
【补充】习题一
写一个类,有个name属性,如果name赋值为字符串,就不让放
【1】方式一
class MyClass:
def __init__(self):
self._name = None
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("Name must be a string.")
self._name = value
- 在上述代码中
- 我们定义了一个名为MyClass的类,它具有一个name属性。
- 在name属性的setter方法中,我们首先检查赋值给name的值是否为字符串
- 如果不是,则抛出一个ValueError的异常。
- 如果是一个字符串,才将其赋值给_name成员变量。
- 通过以上代码
- 当您尝试将非字符串的值赋给name属性时
- 将会得到一个合适的错误提示。
- 示例如下:
obj = MyClass()
obj.name = "John" # 正确赋值
obj.name = 123 # 试图将非字符串赋值给name属性
# 输出:ValueError: Name must be a string.
- 请注意
- 我们使用@property装饰器和setter方法来实现对name属性的访问控制
- 使得赋值操作符(
=
)能够触发setter方法进行验证。
【2】方式二
class MyClass:
def __init__(self):
self._name = None
def __setattr__(self, name, value):
if name == 'name' and not isinstance(value, str):
raise ValueError("Name must be a string.")
object.__setattr__(self, name, value)
def __getattr__(self, name):
if name == 'name':
return self._name
else:
raise AttributeError(f"'MyClass' object has no attribute '{name}'")
- 在上述代码中
- 我们重写了
__setattr__()
方法,当尝试给name属性赋值时,会首先检查该值是否为字符串类型。- 如果不是字符串类型,则抛出一个ValueError异常。
- 如果是字符串类型,才通过调用父类的
__setattr__()
方法来完成赋值操作。
- 我们重写了
- 我们还重写了
__getattr__()
方法- 以便获取name属性的值。
- 如果尝试获取其他属性
- 将会抛出一个AttributeError的异常。
- 使用以上代码
- 您可以对name属性进行控制,并得到适当的错误提示。
- 示例如下:
obj = MyClass()
obj.name = "John" # 正确赋值
obj.name = 123 # 试图将非字符串赋值给name属性
# 输出:ValueError: Name must be a string.
print(obj.name) # 获取name属性的值
# 输出:John
print(obj.age) # 试图获取其他属性
# 输出:AttributeError: 'MyClass' object has no attribute 'age'
【补充】习题二
通过上下文管理器写个MySQL的链接,通过with管理
【1】方式一
- 当使用上下文管理器来管理 MySQL 数据库连接时,可以使用
contextlib
模块的contextmanager
装饰器。 - 下面是一个使用上下文管理器连接 MySQL 数据库的示例代码:
import pymysql
from contextlib import contextmanager
@contextmanager
def mysql_connection(host, port, username, password, database):
conn = pymysql.connect(
host=host,
port=port,
user=username,
password=password,
database=database
)
try:
yield conn
finally:
conn.close()
# 使用示例:
# 请修改以下参数为您实际的 MySQL 连接信息
host = 'localhost'
port = 3306
username = 'your_username'
password = 'your_password'
database = 'your_database_name'
with mysql_connection(host, port, username, password, database) as conn:
# 在这个代码块中,可以执行需要使用数据库连接的操作
cursor = conn.cursor()
cursor.execute("SELECT * FROM your_table_name")
rows = cursor.fetchall()
for row in rows:
print(row)
- 在上述示例代码中
- 我们首先使用
contextmanager
装饰器装饰了一个生成器函数mysql_connection
。 - 在该生成器函数中,我们创建了一个 MySQL 数据库连接,并将其作为上下文管理器的结果进行返回。
- 我们首先使用
- 然后
- 在使用上下文管理器的代码块中,我们可以直接使用
conn
变量得到数据库连接。 - 在这个代码块中,您可以执行需要使用数据库连接的操作。
- 完成后,无论是正常执行还是发生异常,上下文管理器都会确保数据库连接在最后被正确关闭。
- 在使用上下文管理器的代码块中,我们可以直接使用
【2】方式二
- 使用魔法方法
__enter__
和__exit__
- 我们可以实现一个支持上下文管理的 MySQL 数据库连接类。
- 下面是使用魔法方法完成的代码示例:
import pymysql
class MySQLConnection:
def __init__(self, host, port, username, password, database):
self.host = host
self.port = port
self.username = username
self.password = password
self.database = database
self.conn = None
def __enter__(self):
self.conn = pymysql.connect(
host=self.host,
port=self.port,
user=self.username,
password=self.password,
database=self.database
)
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if self.conn:
self.conn.close()
# 使用示例:
# 请修改以下参数为您实际的 MySQL 连接信息
host = 'localhost'
port = 3306
username = 'your_username'
password = 'your_password'
database = 'your_database_name'
with MySQLConnection(host, port, username, password, database) as conn:
# 在这个代码块中,可以执行需要使用数据库连接的操作
cursor = conn.cursor()
cursor.execute("SELECT * FROM your_table_name")
rows = cursor.fetchall()
for row in rows:
print(row)
- 在上述示例代码中,我们定义了一个
MySQLConnection
类,该类包含了__enter__
和__exit__
方法。 - 在
__enter__
方法中,我们创建了一个 MySQL 数据库连接,并返回该连接对象。 - 在
__exit__
方法中,我们在代码块执行完毕后关闭了数据库连接。 - 通过使用这个自定义的上下文管理器类,您可以在
with
语句块中方便地使用数据库连接,并确保在代码块执行完毕后自动关闭连接。
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17581823.html