Python中动态属性和特性
一、使用动态属性访问JSON类数据
feed['Schedule']['events'][40]['name'],
这种句法冗长,在Javascript中,
可以使用feed.Schedule.events[40].name,获取那个值。
在Python中,可以实现一个近似字典的类,达到同样的效果。
from collections import abc
class FrozenJSON:
def __init__(self,mapping):
self.__data = dict(mapping)
def __getattr__(self, name):
if hasattr(self.__data, name):
return getattr(self.__data, name)
else:
return FrozenJSON.build(self.__data[name])
@classmethod
def build(cls,obj):
if isinstance(obj, abc.Mapping):
return cls(obj)
elif isinstance(obj, abc.MutableSequence):
return [cls.build(item) for item in obj]
else:
return obj
FrozenJSON类的关键是__getattr__方法。(仅当无法使用常规的方式获取属性,即在实例、类或超类中找不到指定的属性,解释器才会调用特殊的__getattr__方法)
FrozenJSON.build方法。能深入JSON数据的嵌套结构,使用类方法build把每一层嵌套转换成一个FrozenJSON实例。
二、处理无效属性名
对名称为Python关键字的属性做特殊处理
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import keyword
class FrozenJSON:
def __init__(self,mapping):
self.__data = {}
for key,value in mapping.items():
if keyword.iskeyword():
key += '_'
self.__data[key] = value
对于数字开头的key,str类提供的s.isidentifier()方法,能根据语言的语法判断s是否为有效的Python标识符。
解决办法:抛出异常或者换成通用名称,attr_0,attr_1。
三、使用__new__方法灵活的方式创建对象
通常把__init__称为构造方法,这是从其他语言借鉴过来的术语。其实,用于构建实例的是特殊方法__new__;
这是一个类方法,使用特殊方式处理,因此不必使用@classmethod装饰器,必须返回一个实例。
返回的实例会作为第一个参数(即self)传给__init__方法。
因为调用__init__方法时要传入实例,而且禁止返回任何值,所以__init__方法其实是“初始化方法”。真正的构造方法是__new__。
我们不要自己编写__new__方法,因为从object类集成的实现已经足够了。
class FrozenJSON:
def __new__(cls, arg):
if isinstance(arg,abc.Mapping):
return super().__new__(cls)
elif isinstance(arg, abc.MutableSequence):
return [cls.build(item) for item in arg]
else:
return arg
四、调整OSCON数据源的结构
对象的__dict__属性中存储着对象的属性,前提是类中没有声明__slots__属性。
更新实例的__dict__属性,把值设为一个映射,能快速地在那个实例中创建一堆属性。
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Record:
def __init__(self,**kwargs):
self.__dict__.update(**kwargs)
def load_db(db):
raw_data = data.load()
for collection,rec_list in raw_data['Schedule'].items():
record_type = collection[:-1]
for record in rec_list:
key = '{}.{}'.format(record_type,record['serial'])
record['serial'] = key
db[key] = Record(**kwargs)
五、特性全解析
内置的property经常用作装饰器,但它其实是一个类。在Python中,函数和类通常可以互换,因为二者都是可调用的对象。
property构造方法的完整签名如下:
property(fget=None, fset=None, fdel=None, doc=None)
class LineItem:
def __init__(self,description,weight,price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
def get_weight(self):
return self.__weight
def set_weight(self,value):
if value > 0 :
self.__weight = value
else:
raise ValueError('Value must be > 0')
weight = property(get_weight,set_weight)
特性会覆盖实例属性
特性都是类属性,但是特性管理的其实是实例属性的存取。
(1)当实例和类有同名数据属性时,那么实例属性会覆盖类属性;
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Foo:
data = 'the Foo data attr'
@property
def prop(self):
return 'the prop value'
>>> f = Foo()
>>> vars(f)
{}
>>> f.data
'the Foo data attr'
>>> f.data = 'Bar'
>>> vars(f)
{'data':'Bar'}
>>> Foo.data
'the Foo data attr'
vars函数返回obj的__dict__属性,表明没有实例属性。
(2)实例属性不会覆盖类特性
>>> f = Foo()
>>> Foo.prop
<property object at 0x00000234D2F473B8>
>>> f.prop
'the prop value'
>>> f.prop = 'fooooo'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> f.__dict__['prop'] = 'fooooo'
>>> vars(f)
{'prop': 'fooooo'}
>>> f.prop
'the prop value'
>>> Foo.prop
<property object at 0x00000234D2F473B8>
>>> Foo.prop = 'barrrrr'
>>> f.prop
'fooooo'
直接从Foo中读取prop特性,获取的是特性对象本身,不会运行特性的读值方法。
(3)新添加的类特性覆盖现有实例属性
>>> f.data
'bar'
>>> Foo.data
'the Foo data attr'
>>> Foo.data = property(lambda self:'xxxxxxxxxxx')
>>> f.data
'xxxxxxxxxxx'
>>> del Foo.data
>>> f.data
'bar'
▲ obj.attr这样的表达式不会从obj开始寻找attr,而是从obj.__class__开始,而且,仅当类中没有名为attr的特性时,Python才会在obj实例中寻找。
特性的文档:
控制台中的help()函数或IDE等工具需要显示特性的文档时,会从特性的__doc__属性中提取信息。
为property对象设置文档字符串的方法时传入doc参数:
weight = property(get_weight,set_weight, doc='weight in kilograms')
使用装饰器创建property对象时,读值方法的文档字符串作为一个整体,变成特性文档。
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Foo:
@property
def bar(self):
'''The bar attrbute'''
return self.__dict__['bar']
@bar.setter
def bar(self,value):
self.__dict__['bar'] = value
print(help(Foo.bar))
特性工厂:
def quantity(storage_name):
def qty_setter(instance,value):
if value > 0:
instance.__dict__[storage_name] = value
else:
raise ValueError('Value must be > 0')
def qty_getter(instance):
return instance.__dict__[storage_name]
return property(qty_getter,qty_setter)
class LineItem:
weight = quantity('weight')
price = quantity('price')
def __init__(self,description,weight,price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
属性删除操作:
'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class BlackKnight:
def __init__(self):
self.members = ['an arm', 'another arm',
'a leg', 'another leg']
self.phrases = ["'Tis but a scratch.",
"It's just a flesh wound.",
"I'm invincible!",
"All right, we'll call it a draw."]
@property
def member(self):
print('next member is:')
return self.members[0]
@member.deleter
def member(self):
text = 'BLACK KNIGHT (loses {})\n-- {}'
print(text.format(self.members.pop(0), self.phrases.pop(0)))
六、处理属性的重要属性和函数:
(1)__ class__:
对象所属类的引用(即obj.__class__与type(obj)的作业相同)。
(2)__ dict__:
一个映射,存储对象或类的可写属性。有__dict__属性的对象,任何时候都能随意设置新属性。
如果类有__slots__属性,它的实例可能没有__dict__属性。
(3)__ slots__:
类可以定义这个属性,限制实例能有哪些属性。__slots__属性的值是一个字符串组成的元组,指明允许有的属性。
如果__slots__中没有__dict__,那么该类的实例没有__dict__属性,实例只允许指定民称的属性。
内置函数:
(1)dir([object]):
列出对象的大多数属性。
(2)getattr(object, name[, default]):
从object对象中获取name字符串对应的属性。获取的属性可能来自对象所属的类或超类。
如果没有指定的属性,getattr函数抛出AttributeError异常,或者返回default参数的值。
(3)hasattr(object, name):
如果object对象中存在指定的属性,或者能以某种方式(继承)通过object对象获取指定的属性,返回True。
(4)setattr(object, name, value):
把object对象指定的属性值设为value,前提是object对象能接受那个值。这个函数可能会创建一个新属性,或者覆盖现有的属性。
(5)vars([object])
返回object对象的__dict__属性,如果实例所属的类定义了__slots__属性,实例没有__dict__属性,那么vars函数不能处理那个实例!
相反dir函数能处理这样的实例。
如果没有指定参数,那么vars()函数和locals()函数一样,返回表示本地作用域的字典。
处理属性的特殊方法:
(1)__ delattr__(self, name):
只要使用del语句删除属性,就会调用这个方法。
(2)__ dir__(self):
把对象传给dir函数时调用,列出属性。
(3)__ getattr__(self, name):
仅当获取指定的属性失败,搜索过obj、class和超类之后调用。
(4)__ getattribute__(self, name):
尝试获取指定的属性时总会调用这个方法,不过,寻找的属性时特殊属性或特殊方法时除外。
点号与getattr和hasattr内置函数会触发这个方法。如:obj.attr和getattr(obj, ‘attr’, xx)
调用__getattribute__方法且抛出AttributeError异常时,才会调用__getattr__方法。
为了在获取obj实例的属性不导致无限递归,__ getattribute__方法的实现要使用super().__ getattribute__(obj. name)
(5)__ setter__(self, name, value):
尝试设置指定的属性时总会调用这个方法。点号和setattr内置函数会触发这个方法。
obj.attr = xx 和 setter(obj,'attr',xx)
本文来自博客园,作者:I'm_江河湖海,转载请注明原文链接:https://www.cnblogs.com/jhhh/p/16762614.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏