在Python中,属性自省是一种强大的特性,它允许我们在运行时检查和操作对象的属性。在本文中,我们将探讨Python中的属性自省,并结合实际场景和代码来展示其用法。
(资料图片)
1. 私有属性与访问限制Python中的私有属性是指以下划线开头的变量或方法,例如_name
或_method()
。尽管这种命名约定并非强制性的,只是一种惯例,但它用于提示该属性或方法应该只在类内部使用,而不应该在外部直接访问。
Python中的私有属性具有以下特点:
私有属性不能被外部直接访问,但可以通过类内部的方法间接地访问。
在类内部定义的方法可以访问所有属性,包括私有属性,因为它们都在同一个作用域内。
子类无法继承父类的私有属性,但可以通过公有方法来访问父类的私有属性。
接下来勇哥带你探讨如何访问和修改私有属性:
class YongeGe: name= "勇哥" # 普通属性 _ager = 100 # 普通属性 __money = 109 # 私有属性 __money_empty__ = 1 # 不推荐做法yongge = YongeGe()print(yongge .name) # 输出: 勇哥print(yongge ._ager ) # 输出: 100print(yongge ._YongeGe__money ) # 输出: 109 print(yongge .__money_empty__ ) # 输出: 1print(yongge .__money ) # 输出: AttributeError: "YongeGe" object has no attribute "__money"
很明显看到 Python 中没有真正的私有属性
,实际上双下划线开头的属性被名称重整了,即将属性名转换为_ClassName__name
的形式,使其难以在外部被访问。通过_YongGe__money
可以间接访问私有属性__money
。但是一般不要用,就比如你爹告诉你剩100块了,要省点花,你非要点个海底捞一把梭哈!
总之,Python的私有属性机制主要基于命名约定,其作用是限制属性的可见性和访问性,从而提高代码的封装性和安全性。然而,需要注意的是,这种机制只是一种建议性的规范,并不能完全避免私有属性被访问的可能性。
2. 属性自省与对象内部状态查看在实际开发中,我们经常需要查看对象的属性和方法,以便理解其内部状态。Python提供了几种属性自省的方法,使我们能够方便地检查对象的属性。
2.1 使用__dict__
属性__dict__
是对象的一个属性,它包含了对象的所有属性和方法。通过访问__dict__
,我们可以查看对象的内部状态。
例如,我们定义了一个名为Person
的类,其中包含name
和age
两个属性,以及一个say_hello
方法。下面的代码展示了如何通过访问__dict__
来查看对象的属性和方法:
class YongeGe: def __init__(self, name, money): self.name = name self.money= money def money(self): print(f"Hello, my name is {self.name}.I have {self.money} dollar ")yongge = YongeGe("勇哥", "10")yongge .money= "1010"yongge .__dict__["age"] = "18"
输出结果为:
{"name": "勇哥", "money": "1010", "age": "18"}
我们可以看到对象yongge
的属性和方法,包括name
、age
、money
以及money
方法。
需要注意的是,实例对象的__dict__
属性只能访问和修改其自身的属性和方法,而无法访问其所属类的属性和方法。如果需要访问类的属性和方法,可以通过类的__dict__
属性来实现。
例如,我们可以通过访问Person.__dict__
来查看类Person
的所有属性和方法:
print(yongge .__dict__)
输出结果为:
{"__module__": "__main__", "__init__": , "money": , "__dict__": , "__weakref__": , "__doc__": None}
上述输出结果中,除了__init__
、say_hello
和__dict__
之外,还包括其他一些特殊属性和方法。通过查看类的__dict__
,我们可以获取到类的所有成员。
dir()
函数除了访问对象的__dict__
属性外,我们还可以使用dir()
函数来查看对象的属性和方法。
dir()
函数返回一个包含对象所有属性和方法名称的列表。它不仅可以用于普通对象,还可以用于模块、类和内置类型等。
例如,下面的代码展示了如何使用dir()
函数查看对象的属性和方法:
print(dir(p1))
输出结果为:
["__class__", "__delattr__", "__dict__", "__dir__", "__doc__", "__eq__", "__format__", "__ge__", "__getattribute__", "__gt__", "__hash__", "__init__", "__init_subclass__", "__le__", "__lt__", "__module__", "__ne__", "__new__", "__reduce__", "__reduce_ex__", "__repr__", "__setattr__", "__sizeof__", "__str__", "__subclasshook__", "__weakref__", "age", "gender", "hometown", "name", "say_hello"]
从输出结果中,我们可以看到对象p1
的属性和方法的名称列表。
__slots__
属性在默认情况下,Python 类的实例会使用一个字典来存储它们的属性。这种方式对于属性很少的对象来说可能会浪费空间。特别是在创建大量实例时,这种空间消耗会变得更明显。
为了解决这个问题,我们可以通过在类定义中使用__slots__
来覆盖默认的__dict__
行为。__slots__
是一个特殊属性,它接受一个属性名称的序列,并且在每个实例中只为这些属性保留足够的空间来存储属性值。因为没有为每个实例创建__dict__
,所以可以节省空间。
class YongeGe: # 定义 __slots__属性,让类实例只能绑定 __slots__中指定的属性,且不能添加其他属性 __slots__ = ("name", "money") def __init__(self, name_, money_): self.name = name_ self.money = money_if __name__ == "__main__": yongge = YongeGe("勇哥", 100) print(yongge.name) setattr(yongge, "new_attr", 100) # 输出:AttributeError: "YongeGe" object has no attribute "new_attr"
从上面的代码可以看到,在类中绑定了__slots__后,会限制类实例的灵活性,不能动态添加新的属性,所以在使用这玩意的时候,要仔细想想要灵活还是要内存?
2.4 自定义属性访问还可以通过定义一些特殊方法来自定义类实例的属性访问行为。这些特殊方法可以控制属性的获取、设置和删除操作。
__getattribute__
方法:获取一个属性时,Python 会首先调用该方法。我们可以在这个方法中自定义属性的获取行为。例如,可以在方法中检查属性是否存在,并返回相应的值。
def __getattribute__(self, data): value = super().__getattribute__(data) return value
__setattr__
方法:设置一个属性时,Python 会调用该方法。我们可以在这个方法中自定义属性的设置行为。例如,可以在方法中对属性进行类型检查,然后使用super().__setattr__()
方法设置属性的值。
def __setattr__(self, key, value): super().__setattr__(key, value)
__delattr__
方法:删除一个属性时,Python 会调用该方法。我们可以在这个方法中自定义属性的删除行为。例如,可以在方法中执行一些清理操作或抛出异常。
def __delattr__(self, item): super().__delattr__(item)
__getattr__
方法:获取一个不存在的属性时,Python 会调用该方法。我们可以在这个方法中自定义对不存在属性的处理逻辑。例如,可以返回默认值或者抛出异常。
def __getattr__(self, item): pass
以上是一些常用的自定义属性访问的方法。瞧瞧下面语句的执行顺序:
class MyClass: def __init__(self): self._data = {} def __getattribute__(self, name): # 自定义获取属性的行为 if name == "attribute1": return "Custom Value" elif name == "attribute2": return self._data[name] # 注意,这里会继续执行一次 __getattribute__ else: return super().__getattribute__(name) def __setattr__(self, name, value): # 自定义设置属性的行为 if name == "attribute2": print("__setattr__", name, value) self._data[name] = value + 10 else: super().__setattr__(name, value) def __delattr__(self, name): # 自定义删除属性的行为 if name == "attribute3": self._data.pop(name, None) else: super().__delattr__(name) def __getattr__(self, name): # 处理不存在的属性 return f"Attribute "{name}" does not exist."if __name__ == "__main__": obj = MyClass() obj.attribute2 = 5 res = obj.attribute2 print(res) # 输出: __setattr__ attribute2 5 ;15 print(obj.attribute1) # 输出: Custom Value del obj.attribute3 print(obj.attribute3) # 输出: Attribute "attribute3" does not exist.
在上面的示例中,我们创建了一个名为MyClass
的类,其中定义了__getattribute__
、__setattr__
、
__delattr__
和__getattr__
四个方法来自定义属性访问行为。通过在这些方法中编写相应的逻辑,我们可以控制属性的获取、设置和删除操作,从而实现自定义的属性访问行为。
通过自定义属性访问,我们可以更灵活地控制类实例的属性操作,满足特定的需求,并增强代码的可读性和可维护性。
2.5 使用getattr()
、setattr()
和hasattr()
函数除了查看对象的属性和方法外,我们还可以使用getattr()
、setattr()
和hasattr()
函数来动态地访问和修改对象的属性。
getattr(obj, attr)
函数用于获取对象obj
的属性attr
的值。
setattr(obj, attr, value)
函数用于设置对象obj
的属性attr
的值为value
。
hasattr(obj, attr)
函数用于检查对象obj
是否具有属性attr
,如果有返回True
,否则返回False
。
这些函数提供了一种灵活的方式来操作对象的属性,特别适用于需要在运行时根据条件访问或修改属性的情况。
例如,我们可以通过调用getattr()
函数来获取对象的属性值:
name_value = getattr(yongge, "name")print(name_value)
输出结果为:
勇哥
上述代码中,我们使用getattr(yongge, "name")
来获取对象p1
的name
属性的值。
类似地,我们可以使用setattr()
函数来设置对象的属性值:
setattr(yongge, "age", 40)print(yongge.age)
输出结果为:
40
上述代码中,我们使用setattr(yongge, "age", 40)
将对象yongge
的age
属性值设置为30
,然后再次打印yongge.age
,可以看到属性值已被修改。
另外,我们还可以使用hasattr()
函数来检查对象是否具有某个属性:
has_money = hasattr(yongge, "money")print(has_money)
输出结果为:
True
使用hasattr(yongge, "money")
检查对象yongge
是否具有money
属性,结果为True
,说明对象具有该属性。
通过上述几种方法,我们可以方便地进行属性自省,查看和修改对象的属性,从而更好地理解和操作代码。
3. 来点实际场景假设我们正在开发一个电商网站,需要处理用户购物车的相关逻辑。购物车中的商品信息以字典的形式存储在用户对象的属性中。我们需要编写一个函数,用于计算购物车中商品的总价值。
定义一个User
类,包含一个cart
属性用于存储购物车中的商品信息:
class User: def __init__(self, name): self.name = name self.cart = {} def add_to_cart(self, item, price): self.cart[item] = price
定义一个calculate_cart_value()
函数,用于计算购物车中商品的总价值。在函数内部,我们可以通过属性自省来获取购物车中的商品信息,并计算总价值:
def calculate_cart_value(user): cart = user.__dict__.get("cart", {}) total_value = sum(cart.values()) return total_value
在上述代码中,我们使用user.__dict__.get("cart", {})
来获取购物车信息,如果用户对象中不存在cart
属性,则返回一个空字典。然后,我们使用sum(cart.values())
来计算购物车中商品价格的总和。
下面是使用上述代码的示例:
# 创建用户对象user = User("勇哥")# 添加商品到购物车user.add_to_cart("apple", 10)user.add_to_cart("banana", 5)user.add_to_cart("orange", 8)# 计算购物车总价值total_value = calculate_cart_value(user)print(total_value) # 输出: 23
通过使用属性自省,我们可以方便地访问购物车信息,并计算总价值。属性自省还是挺重要的,面试也问得多,实在不会用也要背好飞机大炮的制作流程。但是这个玩意也不要滥用哟,频繁动态访问,会有一定的性能开销哟~
总结标签: