python的属性自省
私有属性
Python 中的私有属性(private attribute)通常指以下划线开头的变量或方法,如 _name
或 _method()
。这种命名约定并不是强制性的,只是一种惯例,用于提示该属性或方法应该只在类内部使用,而不应该在外部直接访问。
具体来说,Python 中的私有属性具有如下特点:
私有属性不能被外部直接访问,但可以通过类内部的方法间接地访问。
在类内部定义的方法可以访问所有属性,包括私有属性,因为它们都在同一个作用域内。
子类无法继承父类的私有属性,但可以通过公有方法来访问父类的私有属性。
需要注意的是,虽然 Python 中没有真正的私有属性,但可以通过双下划线开头的命名方式(如 __name
)来实现名称重整(name mangling)功能,即将属性名转换为 _ClassName__name
的形式,使其难以在外部被访问。不过这种方式仍然存在安全漏洞和易读性问题,因此不建议滥用。
总之,Python 的私有属性机制主要是基于命名约定的,其作用是限制属性的可见性和访问性,从而提高代码的封装性和安全性。但需要注意的是,这种机制只是一种建议性的规范,并不能完全避免私有属性被访问的可能。
"""
直接访问 _classname__variable_name 中的名称等方法来访问和修改私有属性
"""
class MyClass:
one_attar = 'abc'
_two_attr = 100
# python中私有并没有实现真正的私有,只是在保存属性的时候改了个名字,在外部无法直接访问
__three_attr= 300 # 私有属性
__four_attr__ = 400 # 不推荐做法
class Person:
def __init__(self, name_, age_):
self.__name = name_
self.__age = age_
def get_name(self):
return self.__name
def get_age(self):
return self.__age
def set_name(self, name_):
self.__name = name_
def set_age(self, age_):
self.__age = age_
if __name__ == '__main__':
obj1 = MyClass()
print(obj1.one_attar)
print(obj1._two_attr)
# print(obj1.__three_attr)
print(obj1._MyClass__three_attr)
print(obj1.__four_attr__)
per1 = Person('lemon', 19)
# print(per.__name)
print(per1.get_name())
per1.set_name('keyou')
print(per1.get_name())
per1.sex = '男'
# 输出
abc
100
300
400
lemon
keyou
__dict__
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"Hello, my name is {self.name}. I'm {self.age} years old.")
p1 = Person("Alice", 25)
p1.gender = "female"
p1.__dict__["hometown"] = "New York"
print(p1.__dict__)
print(Person.__dict__.keys())
# 输出
{'name': 'Alice', 'age': 25, 'gender': 'female', 'hometown': 'New York'}
dict_keys(['__module__', '__init__', 'say_hello', '__dict__', '__weakref__', '__doc__'])
现在,我们可以通过访问 p1.__dict__
来查看该对象的所有属性和方法,它应该包含以下键值对:
{
"name": "Alice",
"age": 25,
"gender": "female",
"hometown": "New York",
"say_hello": <function Person.say_hello at 0x7f9a61e4db80>
}
从中可以看到,除了 name
和 age
属性之外,我们还添加了 gender
和 hometown
属性,并且 say_hello
方法也被包含在其中。
注意:实例对象的
__dict__
属性只能访问和修改其自身的属性和方法,而无法访问其所属类的属性和方法。如果需要访问类的属性和方法,可以通过类的__dict__
属性来实现
例如:
print(Person.__dict__.keys()) # 返回 ['__module__', '__init__', 'say_hello', '__dict__', '__weakref__', '__doc__']
以上代码可以返回 Person 类的所有属性和方法,包括 __init__
、say_hello
和 __dict__
等
__slots__
默认情况下,类的实例有一个字典用于存储属性。这对于具有很少实例变量的对象会浪费空间。当创建大量实例时,空间消耗可能会变得尖锐。
可以通过在类定义中定义___ slots __
来覆盖默认 __dict__
行为。__ slots __
声明接受一个实例变量序列,并在每个实例中只保留足够保存每个变量值的空间。因为没有为每个实例创建 __dict__
,所以节省空间。
定义过slots属性来限制类实例的属性,只能绑定slots指定的属性。不能添加slots之外的属性。
class Person:
__slots__ = ('name', 'age')
def __init__(self, name_, age_):
self.name = name_
self.age = age_
if __name__ == '__main__':
per1 = Person('lemon', 18)
print(per1.name)
setattr(per1, 'attr', 100) # AttributeError: 'Person' object has no attribute 'attr'
自定义属性访问
可以定义下面的方法来自定义类实例的属性访问的含义(访问、赋值或者删除 x.name )。
object.__getattr__
当属性查找不到时会调用
object.__getattribute__
查找属性时,第一时间会调用该方法
object.__setattr__
设置属性时,调用该方法设置属性,
object.__delattr__
在del obj.attr删除属性时触发。
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __getattribute__(self, item):
"""
当获取属性时首先会调用此方法
obj.attr
getattr(obj, 'attr')
:param item:
:return:
"""
value = super().__getattribute__(item)
return value
def __setattr__(self, key, value):
"""
当设置属性时调用此方法
obj.attr = value
setattr(obj, 'attr', value)
:param key:
:param value:
:return:
"""
super().__setattr__(key, value)
def __delattr__(self, item):
"""
当删除属性时,会调用此方法
del obj.attr
delattr(obj, 'attr')
:param item:
:return:
"""
super().__delattr__(item)
def __getattr__(self, item):
"""
当获取的属性不存在时,会调用此方法
:param item:
:return:
"""
pass
if __name__ == '__main__':
one_obj1 = MyClass(10, 20)
print(one_obj1.__dict__)
one_obj1.aa = 200 # 动态添加属性不推荐
print(one_obj1.__dict__)
setattr(one_obj1, 'aaa', 300) # one_obj1.aaa = 300
getattr(one_obj1, 'aaa') # one_obj1.aaa
delattr(one_obj1, 'aaa') # del one_obj1.aaa
print(one_obj1.a) # 获取已经存在的属性
print(one_obj1.zzz) # 获取不存在的属性
# 输出
{'a': 10, 'b': 20}
{'a': 10, 'b': 20, 'aa': 200}
10
None
class AttrExample:
"""
str_attr只能设置为字符串类型
int_attr只能设置为int类型
"""
def __setattr__(self, key, value):
if key == 'str_attr':
if isinstance(value, str):
super().__setattr__(key, value)
else:
raise TypeError("str_attr只能设置为字符串类型")
elif key == 'int_attr':
if isinstance(value, int):
super().__setattr__(key, value)
else:
raise TypeError("int_attr只能设置为int类型")
else:
raise TypeError(f"不能创建这个属性:{key}")
if __name__ == '__main__':
attr_example = AttrExample()
attr_example.str_attr = 'abc'
# attr_example.str_attr = 100 # raise TypeError("str_attr只能设置为字符串类型")
attr_example.int_attr = 200
# attr_example.float_attr = 1.2 # raise TypeError(f"不能创建这个属性:{key}")
以上代码定义了 str_attr
和 int_attr
属性的类 AttrExample,并在 __setattr__
方法中对属性进行了限制:只有当属性名为 str_attr
时,属性值才能为字符串类型;只有当属性名为 int_attr
时,属性值才能为整数类型。如果属性名不为 str_attr
或 int_attr
,则会抛出 TypeError 异常,提示不能创建这个属性。
评论