python对象的三个属性_Python 对象属性的访问

在 Python 中,一切皆对象。属性访问可以理解为是从一个已有的对象中获得另一个对象的方法。对象属性的访问涉及到对象的 __dict__ 属性、描述符等概念,以及 __getattribute__、__getattr__ 等方法。

对象字典属性

Python 中的对象有一个 __dict__ 属性,其是一个字典类型,对象当前可用的属性和方法都保存在合格字典中。它存储着对象属性的名称与值的键值对。示例(在 Python 2.7 环境测试):

>>> class C(object):

... x = 1

...

>>> C.__dict__

dict_proxy({

'__dict__': ,

'x': 1, '__module__': '__console__',

'__weakref__': ,

'__doc__': None

})

>>> c = C()

>>> c.__dict__

{}

>>> c.y = 1

>>> c.__dict__

{'y': 1}

>>> c.x

1

>>> c.x = 2

>>> c.x

2

>>> C.x

1

>>> c.__dict__

{'y': 1, 'x': 2}

由上例应该注意到,类变量 x 存储在类 C 的 dict 属性中,而由 C 初始化的对象 c 的属性 y 则在 c 的 dict 中。对象 c 仍然可以访问其类型 C 中的类变量 x。但是,如果在对象 c 中重新设置属性 x 之后,则 C 与 c 中各自有自己的 x 属性,此时 c.x 不再访问其类的属性,而是访问自己的 x 属性。

还应注意到,类对象的 __dict__ 属性为普通的 dict 类型,而类定义的 __dict__ 则为 dict_proxy 类型(在 Python3 中为 mappingproxy 类型)。类对象的该属性是可以被直接修改的,而类的却不行。因为类的 __dict__ 是只读的,所以其命名中被加入了 proxy 字眼,这样做的目的是为了防止其被意外修改而导致意想不到的错误发生。

>>> c.__dict__['x'] = 5

>>> C.__dict__['x'] = 6

Traceback (most recent call last):

File "", line 1, in

C.__dict__['x'] = 6

TypeError: 'dictproxy' object does not support item assignment

>>> c.x

5

>>> C.x

1

>>> c.__dict__ = {}

>>> C.__dict__ = {}

Traceback (most recent call last):

File "", line 1, in

C.__dict__ = {}

AttributeError: attribute '__dict__' of 'type' objects is not writable

>>> c.__dict__

{}

>>> c.x

1

>>> C.x

1

并不是所有的对象都有 __dict__ 这个属性,例如实现了 __slots__ 属性的类的对象。拥有 __slots__ 属性的类在实例化对象时不会自动分配 __dict__, 只有在 __slots__ 中的属性才能被使用,但它的设置只对对象真正的属性有限制作用。如果是用 property 修饰的属性以及属性是一个描述符对象时是不受限制的。

描述符

描述符是实现了描述符协议的对象,本质上是一种拥有绑定行为的对象属性。描述符的访问行为被如下的描述符协议方法覆盖:

__get__(self, obj, type=None) --> value

__set__(self, obj, value) --> None

__delete__(self, obj) --> None

描述符协议只是一种在模型中引用属性时指定将要发生事件的方法。实现了以上描述符协议三个方法中任意一个的对象即是描述符。同时定义了 __get__ 和 __set__ 方法的对象就叫作数据描述符(Data Descriptor),也被成为资源描述符。而只定义了 __get__ 方法的对象被叫做非数据描述符(Non-data Descriptor)。实际上类方法(classmethod)即为一个非数据描述符。数据描述符与非数据描述会影响其被访问的顺序。如果实例中存在与数据描述符同名的属性,则会优先访问数据描述符。如果实例中存在与非数据描述符同名的属性,则优先访问实例属性。一个描述符的定义类似如下形式:

class Descriptor(object):

def __init__(slef):

pass

def __get__(self, instance, owner):

"""用于访问属性

返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常

"""

pass

def __set__(self, instance, value):

"""用于设置属性值

将在属性分配操作中调用,不会返回任何内容

"""

pass

def __delete__(self, ):

"""用于删除属性

控制删除操作,不会返回内容

"""

pass

描述符将某种特殊类型的类的实例指派给另一个类的属性(注意: 这里是类属性,而不是对象属性,即描述符被分配给一个类,而不是实例)。描述符相当于是一种创建托管属性的方法。托管属性可以用于保护属性不受修改,对传递的值做检查,或自动更新某个依赖属性的值。下面是一个简单的示例:

class Descriptor(object):

def __init__(self, m):

self.m = m

def __get__(self, instance, owner):

return instance.n * self.m

def __set__(self, instance, value):

if value < 0:

raise ValueError("Negative value not allowed:%s" % value)

instance.n = value

class Foo(object):

bar = Descriptor(0)

har = Descriptor(1)

tar = Descriptor(2)

yar = Descriptor(3)

def __init__(self, n):

self.n = n

"""

>>> f = Foo(10)

>>> f.bar

0

>>> f.bar = 100

>>> f.bar

0

>>> f.har

100

>>> f.har = 10

>>> f.har

10

>>> f.yar

30

>>> f.yar = 12345

>>> f.yar

37035

"""

Python 中的类方法装饰器 classmethod、staticmethod 实际上是一个非数据描述符,下面是他们的纯 Python 实现示例:

class StaticMethod(object):

def __init__(self, f):

self.f = f

def __get__(self, instance, owner):

return self.f

class ClassMethod(object):

def __init__(self, f):

self.f = f

def __get__(self, instance, owner):

if owner is None:

owner = type(instance)

def _func(*args):

return self.f(owner, *args)

return _func

此外,Python 的 property 则是一个数据描述符,它将对象属性的访问转化为方法调用。类中的 property 装饰器有一个缺陷,每次试图访问 property 属性时其装饰的函数都会被调用,而有时候可能只希望函数被调用一次。于是,可以模仿 property 来实现一个惰性属性(lazy property),即在必要的时候(属性被真正访问到时)才初始化属性。以下是惰性属性描述符的实现示例:

class lazy_property(object):

def __init__(self, func):

self.func = func

def __get__(self, obj, cls):

if obj is None:

return self

value = obj.__dict__[self.func.__name__] = self.func(obj)

return value

上例中实现一个非数据描述来达到惰性初始化属性的目的。对象惰性属性在被访问时会调用 func 初始化得到 value,然后再在对象的 __dict__ 中设置同名的属性,下一次再访问属性时,会直接返回 __dict__ 中保存的值,而不再去访问描述符。这里涉及到了对象属性的访问优先级顺序问题。

属性访问顺序

Python 在对象属性访问时会无条件调用 __getttribute__() 方法。在属性搜索的优先级链中,类字典中发现的数据描述符的优先级高于实例变量,实例变量优先级高于非数据描述符。如果提供了 __getattr__(),优先级链会为 __getattr__() 分配最低优先级。除非 __getttribute__() 显示调用或者抛出 AttributeError 异常,否则 __getattr__() 将不会被调用。

描述符的调用是通过 _getattribute__() 方法实现的,重写该方法可以阻止描述符的自动调用。数据描述符总是覆盖类实例的 __dict__,而非数据描述符可能会被类实例的 __dict__ 覆盖。_getattribute__() 方法的实现大概类似如下形式:

def __getattribute__(self, key):

"Emulate type_getattro() in Objects/typeobject.c"

v = object.__getattribute__(self, key)

if hasattr(v, '__get__'):

return v.__get__(None, self)

return v

需要注意的是,重写 __getttribute__() 方法时,不能在其实现中使用 self.xxx 的形式访问自己的属性,这样会导致无限递归。而需要访问自己的属性时,应该调用基类的方法。如 object.__getattribute__(self, name)。

下面详细描述下对象属性的访问顺序。假设有 class C, c = C(), 那么 c.x 的执行顺序为:

(1)如果 x 是出现在 C 或其基类的 __dict__ 中,且是数据描述符, 那么调用其 __get__ 方法,否则

(2)如果 x 出现在 c 的 __dict__ 中,那么直接返回 c.__dict__['x'],否则

(3)如果 x 出现在 C 或其基类的 __dict__ 中,那么

(3.1)如果 x 是非数据描述符,那么调用其 __get__ 方法,否则

(3.2)返回 __dict__['x']

(4)如果 C 有 __getattr__ 方法,调用 __getattr__ 方法,否则

(5)抛出 AttributeError

处理缺失值

默认情况下,当访问的属性在对象中不存在时,会抛出 AttributeError 异常。而在有些场景中我们并不希望这样,比如在我工作的项目中,当访问一项配置时,如果该配置项不存在,我们希望其返回 None,而不是发生异常。这用 __getattr__ 方法很容易实现,该方法通常与 __setattr__、__delattr__ 方法配合使用,__setattr__ 方法会改变属性的复制行为:

class Foo(object):

def __init__(self):

self.x = 1

def __getattr__(self, key):

try:

return self.__dict__[key]

except KeyError:

return None

def __setattr__(self, key, value):

self.__dict__[key] = value

def __delattr__(self, key):

try:

del self.__dict__[key]

except KeyError:

return None

如果对象时一个字典,则当访问一个不存在的 key 时,会发生 KeyError 异常。字典也有一个方法可以用来处理缺失值,即 __missing__。这个方法虽然与属性访问无关,这里也做一下简单的介绍。当访问的键不存在时,dict.__getitem__() 方法会自动调用该方法。需要注意的是 dict 中并没这个方法,需要在子类中实现。示例:

class FooDict(dict):

def __missing__(self, key):

self[key] = "hello"

return "hello"

fdict = FooDict()

print fdict

print fdict["bar"]

# 执行结果:

# {}

# hello

可以用该方法来实现一个缺省字典:

class defaultdict(dict):

def __init__(self, default_factory=None, *a, **kw):

dict.__init__(self, *a, **kw)

self.default_factory = default_factory

def __missing__(self, key):

self[key] = value = self.default_factory()

return value

参考资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/501118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JdbcTemplate(操作数据库-添加功能)

目录 JdbcTemplate&#xff08;操作数据库-添加功能&#xff09; 1.建立数据库表&#xff1a; 2.对应数据库创建实体类&#xff1a; 3.编写service 和 dao &#xff08;1&#xff09;在 dao 层进行数据库添加操作 &#xff08;2&#xff09;具体&#xff1a;调用jdbcTemp…

python读取大文件csv_实现读取csv文件,文件里面是有限个百分数成绩(99.6、76.8等等...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 实现读取csv文件&#xff0c;文件里面是有限个百分数成绩&#xff08;99.6、76.8等等&#xff09;导出GPA 和等级代码是这样&#xff0c;但是报错了&#xff0c;在score float(sc)这行就报错了&#xff0c;下面有没有错误还不知道…

JdbcTemplate(操作数据库-修改和删除功能)

目录 JdbcTemplate&#xff08;操作数据库-删除功能&#xff09; 1.创建数据库 2.配置文件实现 3.创建实体类 4.创建dao层 5.创建service层 6.测试类 7.测试结果&#xff1a; JdbcTemplate&#xff08;操作数据库-删除功能&#xff09; 1.创建数据库 user_db数据库的t_…

单片机定时器实验两位倒计时秒表_51单片机基础与应用8天速成(三)

在讲授中断这一概念时&#xff0c;人们总是喜欢举洗衣服烧水的例子&#xff1a;话说&#xff0c;一天“你”独自在家&#xff0c;为了泡脚给自己烧上了一壶水&#xff0c;然后想着明天没有衣服穿了&#xff0c;就去阳台洗起了衣服。过了十几分钟&#xff0c;“你”在阳台洗着衣…

网线制作ppt_快速制作PPT技巧!

为什么同样的PPT&#xff0c;你花费了一天&#xff0c;我却只用了一小时&#xff1f;在我仔细观察了一些制作人员的操作后&#xff0c;总结了如下实用技巧&#xff01;01自定义访问工具栏在PPT中我们有很多的常用操作&#xff0c;例如「左对齐/右对齐」「置于底层/置于顶层」「…

JdbcTemplate(操作数据库-查询返回值)

目录 JdbcTemplate&#xff08;操作数据库-查询返回值&#xff09; 1.创建数据库 2.创建实体类 3.创建dao层 4.创建service层 5.创建测试类&#xff1a; 6.xml配置 7.测试结果&#xff1a; 8.结构示意&#xff1a; JdbcTemplate&#xff08;操作数据库-查询返回值&…

c++opencv显示中文_OpenCV安装,配置和运行

今天小崔有个项目功能想用OpenCV软件库实现一下&#xff0c;就安装了OpenCV,在这里给大家分享一下安装过程。一.什么是OpenCV OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。 它轻量…

qt 当前窗口句柄_QT获取Windows系统所有窗口句柄

#include #include #pragma comment(lib,"user32.lib")/* 回调函数&#xff0c;用于捕获进程 */BOOL MyEnumProc(HWND hwnd, LPARAM param){LPWSTR lpString (LPWSTR)malloc(sizeof(WCHAR) * MAX_PATH);if (IsWindow(hwnd) &&IsWindowEnabled(hwnd) &&a…

JdbcTemplate(操作数据库-查询返回对象、查询返回集合)

JdbcTemplate&#xff08;操作数据库-查询返回对象、查询返回集合&#xff09; 1.创建数据库 数据库中有三条记录&#xff0c;数据库名为user_db&#xff0c;数据库表为t_book 2.新建实体类&#xff1a; Book类中的每一个属性对应数据库中的一条记录 package org.example.sp…

hbase 查询设置超时_hbase master挂掉-zookeeper连接超时原因

并行运行hbase删表&#xff0c;建表操作&#xff0c;多个表多个region&#xff0c;导致hbase挂掉。查看日志&#xff1a;从日志中可以看出GC时间过长导致zookeeper连接超时&#xff0c;master退出。(是master退出而不是regionserver退出是因为进行的操作是建表&#xff0c;删表…

机器学习如何计算特征的重要性_机器学习之特征工程

特征选择是特征工程中的一个子集&#xff0c;从所有的特征中&#xff0c;选择有意义的&#xff0c;对模型有帮助的特征&#xff0c;以避免将所有特征中对模型没作用的特征导入模型去训练&#xff0c;消耗不必要的计算资源。更正式地说&#xff0c;给定n个特征&#xff0c;我们搜…

白中英 计算机组成原理_计算机组成原理 第五版.立体化教材 白中英 大学教材...

目 录第1章 计算机系统概论第2章 运算方法和运算器第3章 多层次的存储器第4章 指令系统第5章 中央处理器第6章 总线系统第7章 外存与IO设备第8章 输入输出系统第9章 并行组织与结构关于我们大学生必备资源库为大学生提供网课答案、大学课后答案、软件安装、大学考试考证资源以及…

Spring事务操作-事务

目录 Spring事务操作-事务 1.什么是事务 &#xff08;1&#xff09;典型场景 2.事务的四个特性&#xff08;俗称ACID特性&#xff09; &#xff08;1&#xff09;原子性 &#xff08;2&#xff09;一致性 &#xff08;3&#xff09;隔离性 &#xff08;4&#xff09;持久性…

ios 静态库合成_iOS : 静态库(.framework)合并

如果写了一个Framework&#xff0c;根据Build时选择的机器类型&#xff0c;会分为模拟器Framework和真机Framework&#xff0c;两者是不能混用的。此时可以通过配置一个Run Script&#xff0c;在Script中使用lipo命令来合并两个版本的Framework&#xff0c;重新生成一个新的Fra…

python在统计专业的应用_Python:使用Counter进行计数统计

计数统计就是统计某一项出现的次数。实际应用中很多需求需要用到这个模型。比如测试样本中某一指出现的次数、日志分析中某一消息出现的频率等等‘这种类似的需求有很多实现方法。下面就列举几条。 (1)使用dict 看下面代码#codingutf-8 data [‘a‘,‘2‘,2,4,5,‘2‘,‘b‘,4…

Spring事务操作-事务引入

目录 Spring事务操作-事务引入 1.模拟异常 2.测试异常 3.没有使用spring框架的时候异常该如何处理 4.使用spring框架的时候异常该如何处理 5.在spring 进行声明式事务管理&#xff0c;底层使用AOP 6.spring 事务管理API 7.事务操作&#xff08;注解声明式事务管理&…

oracle中取反_oracle正则表达式regexp_like的用法详解

oracle正则表达式regexp_like的用法详解更新时间&#xff1a;2013年06月13日 17:42:05 作者&#xff1a;本篇文章是对oracle正则表达式regexp_like的用法进行了详细的分析介绍&#xff0c;需要的朋友参考下/*ORACLE中的支持正则表达式的函数主要有下面四个&#xff1a;1&…

在r中rowsums_用R进行数据分析-05

20-矩阵与数组一、矩阵的定义矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集。向量是一维的&#xff0c;而矩阵是二维的&#xff0c;需要有行和列。R中&#xff0c;矩阵是有维数的向量&#xff0c;但元素必须拥有相同的模式&#xff0c;此和向量一致…

python字典和集合对象可以进行索引操作_Python中的字典跟集合整理笔记

泛映射类型映射类型&#xff1a;不仅仅是dict&#xff0c;标准库里的所有映射类型都是利用dict来实现的&#xff0c;因此它们有个共同的限制&#xff0c;即只有可散列的数据类型才能用做这些映射的键。(只有键有这个需求&#xff0c;值并不需要必须是可散列的数据类型。)什么是…

JAVA入门级教学之(super的内存示意)

Super的原理&#xff1a; 代表的是当前对象this的父类型特征 如果类加载过程中&#xff0c;这个类有静态代码块&#xff0c;有静态变量&#xff0c;一律都会执行&#xff0c;系统会先分配空间 只要方法调用&#xff0c;就一定会进行方法压栈 new一个方法&#xff0c;其实是调用…