Python属性自省:深入了解属性访问与限制

在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 )  # 输出: 100
print(yongge ._YongeGe__money )  # 输出: 109  
print(yongge .__money_empty__ )  # 输出: 1
print(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的类,其中包含nameage两个属性,以及一个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的属性和方法,包括nameagemoney以及money方法。

需要注意的是,实例对象的__dict__属性只能访问和修改其自身的属性和方法,而无法访问其所属类的属性和方法。如果需要访问类的属性和方法,可以通过类的__dict__属性来实现。

例如,我们可以通过访问Person.__dict__来查看类Person的所有属性和方法:

print(yongge .__dict__)


输出结果为:

{'__module__': '__main__', '__init__': <function YongeGe.__init__ at 0x0000017E50B9C5E0>, 'money': <function YongeGe.money at 0x0000017E50BBADC0>, '__dict__': <attribute '__dict__' of 'YongeGe' objects>, '__weakref__': <attribute '__weakref__' of 'YongeGe' objects>, '__doc__': None}


上述输出结果中,除了__init__say_hello__dict__之外,还包括其他一些特殊属性和方法。通过查看类的__dict__,我们可以获取到类的所有成员。

2.2 使用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的属性和方法的名称列表。

2.3 使用__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 自定义属性访问

还可以通过定义一些特殊方法来自定义类实例的属性访问行为。这些特殊方法可以控制属性的获取、设置和删除操作。

  1. __getattribute__ 方法:
    获取一个属性时,Python 会首先调用该方法。我们可以在这个方法中自定义属性的获取行为。例如,可以在方法中检查属性是否存在,并返回相应的值。

    def __getattribute__(self, data):
        value = super().__getattribute__(data)
        return value
    
    
    
  2. __setattr__ 方法:
    设置一个属性时,Python 会调用该方法。我们可以在这个方法中自定义属性的设置行为。例如,可以在方法中对属性进行类型检查,然后使用 super().__setattr__() 方法设置属性的值。

    def __setattr__(self, key, value):
        super().__setattr__(key, value)
    
    
    
  3. __delattr__ 方法:
    删除一个属性时,Python 会调用该方法。我们可以在这个方法中自定义属性的删除行为。例如,可以在方法中执行一些清理操作或抛出异常。

    def __delattr__(self, item):
        super().__delattr__(item)
    
    
    
  4. __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")来获取对象p1name属性的值。

类似地,我们可以使用setattr()函数来设置对象的属性值:

setattr(yongge, "age", 40)
print(yongge.age)


输出结果为:

40


上述代码中,我们使用setattr(yongge, "age", 40)将对象yonggeage属性值设置为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


通过使用属性自省,我们可以方便地访问购物车信息,并计算总价值。属性自省还是挺重要的,面试也问得多,实在不会用也要背好飞机大炮的制作流程。但是这个玩意也不要滥用哟,频繁动态访问,会有一定的性能开销哟~

总结

以上就是我今天为各位小伙伴准备的内容,如有误,请包涵并指出修改。


如果你对Python感兴趣,想要学习python,这里给大家分享一份Python全套学习资料,都是我自己学习时整理的,希望可以帮到你,一起加油!

😝有需要的小伙伴,可以V扫描下方二维码免费领取🆓

1️⃣零基础入门

① 学习路线

对于从来没有接触过Python的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

② 路线对应学习视频

还有很多适合0基础入门的学习视频,有了这些视频,轻轻松松上手Python~
在这里插入图片描述

③练习题

每节视频课后,都有对应的练习题哦,可以检验学习成果哈哈!
在这里插入图片描述

2️⃣国内外Python书籍、文档

① 文档和书籍资料

在这里插入图片描述

3️⃣Python工具包+项目源码合集

①Python工具包

学习Python常用的开发软件都在这里了!每个都有详细的安装教程,保证你可以安装成功哦!
在这里插入图片描述

②Python实战案例

光学理论是没用的,要学会跟着一起敲代码,动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。100+实战案例源码等你来拿!
在这里插入图片描述

③Python小游戏源码

如果觉得上面的实战案例有点枯燥,可以试试自己用Python编写小游戏,让你的学习过程中增添一点趣味!
在这里插入图片描述

4️⃣Python面试题

我们学会了Python之后,有了技能就可以出去找工作啦!下面这些面试题是都来自阿里、腾讯、字节等一线互联网大厂,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
在这里插入图片描述
在这里插入图片描述

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值