前路漫漫,我爱Python。Hello,大家好!上一篇文章(Python必知必会:令人相见恨晚的10个Python类技巧(一))我向大家分享了10个关于Python类的关键技巧的前5个,今天我们继续剩下的5个。助你写出更加优雅、高效和Pythonic的代码!
在Python中,name mangling(名称改编)是用来处理类中的私有属性和方法的一种机制。当类的属性或方法的名称以双下划线(__)开头(但不以双下划线结尾)时,Python会自动将其名称改编为一种特定格式。这种机制的目的是为了避免子类意外覆盖父类的私有属性和方法。
class MyClass:
def __init__(self):
self.__private_attribute = 42
def __private_method(self):
return "This is a private method"
在这个例子中:
self.__private_attribute 被改编为 self._MyClass__private_attributeself.__private_method() 被改编为 self._MyClass__private_method()一起来看一个实际的例子:
class Parent:
def __init__(self):
self.__private_attr: str = "This is private"
def __private_method(self) -> str:
return "This is a private method"
class Child(Parent):
def access_private(self) -> str:
return self.__private_method()
if __name__ == '__main__':
p: Parent = Parent()
c: Child = Child()
print(p._Parent__private_attr) # 直接访问被改编的私有属性
print(p._Parent__private_method()) # 直接调用被改编的私有方法
# print(c.__private_method()) # 这将会报错,因为 __private_method 已被改编
print(c._Parent__private_method()) # 正确访问父类的私有方法
This is private
This is a private method
This is a private method
在上面的例子中:
Parent 类的 __private_attr 和 __private_method 被改编为 _Parent__private_attr 和 _Parent__private_method。Child 类继承了 Parent 类,直接访问 c.__private_method() 会报错,因为该方法已经被改编为 _Parent__private_method。名称改编并不意味着属性或方法完全是私有的。通过知道类的名称和改编规则,还是可以直接访问这些所谓的“私有”属性和方法。因此,名称改编更多的是一种警告(不要直接访问或修改),而不是一种严格的访问控制机制。
在Python中,@property 装饰器用于将类的方法转换为属性。这种机制允许我们定义可管理的属性(managed attributes),即通过方法控制属性的获取、设置和删除操作。这样可以在不改变类的接口的情况下,对属性的访问进行控制和管理。
@property 装饰器
@property 装饰器用于将一个方法转换为一个属性,从而可以像访问普通属性一样访问方法的返回值。它常用于以下几种情况:
@property 来延迟计算,只在需要时才进行。@property 定义可管理的属性
我们可以使用 @property 来定义一个 getter 方法,它使得我们可以像访问普通属性一样访问一个方法的返回值。通常我们会与 @property 搭配 @<property_name>.setter 和 @<property_name>.deleter 一起使用,以定义属性的 setter 和 deleter 方法。
class Rectangle:
def __init__(self, width: int, height: int) -> None:
self._width: int = width
self._height: int = height
@property
def width(self) -> int:
"""Getter method for the width attribute."""
return self._width
@width.setter
def width(self, value: int) -> None:
"""Setter method for the width attribute with validation."""
if value <= 0:
raise ValueError("Width must be positive.")
self._width = value
@property
def height(self) -> int:
"""Getter method for the height attribute."""
return self._height
@height.setter
def height(self, value: int) -> None:
"""Setter method for the height attribute with validation."""
if value <= 0:
raise ValueError("Height must be positive.")
self._height = value
@property
def area(self) -> int:
"""Read-only property that computes the area of the rectangle."""
return self.width * self.height
if __name__ == '__main__':
rect: Rectangle = Rectangle(width=10, height=5)
print(rect.width) # 输出: 10
print(rect.height) # 输出: 5
print(rect.area) # 输出: 50
rect.width = 20
print(rect.width) # 输出: 20
print(rect.area) # 输出: 100
# rect.area = 200 # 会报错: AttributeError: property 'area' of 'Rectangle' object has no setter
在这个例子中,我们通过 @property 定义了可管理的属性,提供了一种优雅的方法来控制对类属性的访问和修改,同时保持了简洁的接口。
总之,@property 和可管理的属性在Python中非常有用,可以让我们定义类时隐藏复杂的实现细节,提供一种更清晰和易于使用的接口。通过 @property,我们可以轻松实现属性的延迟计算、数据校验、只读属性等功能,而不需要改变类的外部接口。
type() 定义类
通常情况下,我们都是使用 class 关键字来创建类:
class Dog:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
def bark(self) -> None:
print('Woof!')
但是,我们可以使用 type() 方法动态地创建类。其语法如下:
type(name, bases, dict)
name 是表示类名称的字符串。bases 是该类继承的父类元组。dict 是包含该类所有方法的字典。下面我们通过 type() 方法来动态地创建相同的类:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
def bark(self) -> None:
print('Woof!')
if __name__ == '__main__':
methods_dict: dict = {'__init__': __init__, 'bark': bark}
Dog = type('Dog', (), methods_dict)
dog: Dog = Dog(name='Akita', age=5)
print(dog.__dict__) # {'name': 'Akita', 'age': 5}
dog.bark() # Woof!
如果我们想要在不使用 class 关键字的前提下动态地创建类,这种方法就非常适用。
__slots__ 属性
在Python中,类的 __slots__ 属性允许我们定义在对象上允许的属性,从而实现对属性数量的控制。
在下面的示例中,只能在 Person 对象上定义 name 和 age 属性。如果我们尝试定义这两种属性以外的其他属性,比如,性别(gender),则会发生错误:
class Person:
__slots__: list[str] = ['name', 'age']
if __name__ == '__main__':
person = Person()
person.name = 'Jack'
person.age = 25
print(f'Name: {person.name}, Age: {person.age}') # Name: Jack, Age: 25
person.gender = 'Male' # AttributeError: 'Person' object has no attribute 'gender'
如果我们想要严格限制类的属性以确保开发中的结构,那这种方式非常有用。
如果我们在类中定义了 __cal__ 魔术方法,那么类也可以用作装饰器。
当我们像函数一样调用对象时,__cal__ 魔术方法定义了对象的行为。
在下面的示例中,我们定义了一个装饰器类,在被装饰函数的返回值后面添加一个符号(感叹号):
class SymbolAppend:
def __init__(self, symbol: str):
self.symbol = symbol
def __call__(self, func):
def wrapper(name):
return func(name) + self.symbol
return wrapper
@SymbolAppend('!')
def greet(city: str) -> str:
return f'Welcome to {city}'
if __name__ == '__main__':
print(greet('Beijing')) # Welcome to Beijing!
在这里:
SymbolAppend('!') 发生时,会创建一个 SymbolAppend 对象,其中 symbol = '!'。@append(‘!’) 与 greet =append(‘!’)(greet) 的效果相同。__call__ magic 方法被调用,并将 greet 重新分配给 wrapper,在返回值后面添加感叹号。如果要使用函数作为装饰器实现相同的目的,则会涉及双嵌套:
def symbol_append(symbol: str):
def decorator(func):
def warpper(name):
return func(name) + symbol
return warpper
return decorator
@symbol_append("!")
def greet(city: str) -> str:
return f'Welcome to {city}'
if __name__ == '__main__':
print(greet('Beijing')) # Welcome to Beijing!
从上面的对比可以看出,使用类作为装饰器可以降低代码的复杂性,同时增强其可读性和简洁性。
好啦,关于Python类的10个技巧就分享到这啦!感谢你的阅读,希望对你有所帮助。
推荐阅读👇👇👇
Python必知必会:令人相见恨晚的10个Python类技巧(一)
Python类型提示(type hints):提升代码质量与可读性的利器!墙裂推荐~~
9个令人相见恨晚的Python字典技巧,让你写出更优雅、更Pythonic的代码!
- END -
🌟 如果你喜欢这篇文章并且愿意支持我的话,你可以: 🌟
👍👍👍 点赞文章 ⤴️⤴️⤴️ 转发文章 ⚛️⚛️⚛️ 点击在看 🧑🤝🧑🧑🤝🧑🧑🤝🧑 让文章被推荐给更多人,找到更多志同道合的伙伴 👏👏👏 欢迎给我留言 关注我,第一时间接收更多关于Python、数据分析和机器学习的深入分析和最新趋势。我们一起探索数据的力量,解锁AI的潜能。你的每一次互动都是对我的极大鼓励,也是我们共同成长的动力!
👇👇👇 关注并开启星标提醒,让知识与技能的火花不断在你的职业道路上燃烧吧!
原文作者:数据派探险家;如有侵权,请联系管理员删除。