python属性重新赋值叫啥
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python属性重新赋值叫啥相关的知识,希望对你有一定的参考价值。
python属性重新赋值叫就是在类obj 生成对象A 的时候,进行初始化,自动调用了obj 的init函数,声明了一个对象A的私有变量,并对其赋值。这时候的A.a就是访问了一个对象的私有变量。对其赋值,没有调用任何其他方法,和b=“test”一样,就是普通的对字符串赋值,这个时候,a就是一个对象A的一个私有变量。 参考技术A 在之前文章中写了一节同名的类和实例变量,如果存在同名的类和实例变量的话,属性查找会优先选择实例变量,而描述器会改变python默认的属性查找行为。如果一个类属性是描述器的话,使用实例调用a.attr1这种形式将只会访问类属性,而不会像之前那样访问实例变量,这是怎么回事,通过一个例子看一下:
class Desc1:
def __get__(self, obj, objtype=None):
print('__get__ age')
return self.age
def __set__(self, obj, value):
print('__set__ age')
self.age = value
class A:
age = Desc1()
def __init__(self, age) -> None:
self.age = age
# def __setattr__(self, name: str, value) -> None:
# self.__dict__[name] = value
# print('A __setattr__', name, self.__dict__[name])
a1 = A(1)
a2 = A(2)
print('a1 age:', a1.age)
print('a2 age:', a2.age)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
执行以上代码输出:
__set__ age
__set__ age
__get__ age
a1 age: 2
__get__ age
a2 age: 2
1
2
3
4
5
6
1
2
3
4
5
6
可以看到,a1的age也变成了2,程序访问的是类变量。
python中的属性赋值会受到descriptor(确切说是data descriptor)的影响,同时也会受到__setattr__函数的影响。python内置函数中还有一个setattr,setattr(x, 'foobar', 123) 等价于 x.foobar = 123,属于属性赋值的操作,与descriptor和__setattr__不在一个层级。__setattr__的说明如下:
object.``__setattr__(self, name, value)
此方法在一个属性被尝试赋值时被调用。这个调用会取代正常机制(即将值保存到实例字典)。 name 为属性名称, value 为要赋给属性的值。
可以看到__setattr__会取代正常的属性赋值机制,我们将它激活再看,将上面注释的代码放开:
class A:
age = Desc1()
def __init__(self, age) -> None:
self.age = age
def __setattr__(self, name: str, value) -> None:
self.__dict__[name] = value
print('A __setattr__', name, self.__dict__[name])
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
运行代码,报错了:
A __setattr__ age 1
A __setattr__ age 2
__get__ age
Traceback (most recent call last):
File "d:\code\python_programs\demos\py_class_demos\descriptor_demo1.py", line 25, in <module>
print('a1 age:', a1.age)
File "d:\code\python_programs\demos\py_class_demos\descriptor_demo1.py", line 5, in __get__
return self.age
AttributeError: 'Desc1' object has no attribute 'age'
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
错误提示Desc1对象没有属性age,但同时也可以看到__setattr__生效了.
说明在a1.age获取age的时候调用的还是类属性(描述器),我们存值的时候存入的是实例字典,描述符中应当是没有age的,所以报错。
如此属性age的set和get操作就不对等了,set存入的是实例字典,而get则去描述器中找去了,如何是好。
在python描述符指南中,给出了答案:
实例查找通过命名空间链进行扫描,数据描述器的优先级最高,其次是实例变量、非数据描述器、类变量,最后是 __getattr__() (如果存在的话)。
“数据描述器的优先级最高”,这就是程序会优先去描述器中寻找age的原因,修改代码,将__set__方法注释掉:
class Desc1:
def __get__(self, obj, objtype=None):
print('__get__ age')
return self.age
# def __set__(self, obj, value):
# print('__set__ age')
# self.age = value
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
运行代码输出:
A __setattr__ age 1
A __setattr__ age 2
a1 age: 1
a2 age: 2
1
2
3
4
1
2
3
4
可以看到,代码正常运行了。
下面关于属性查找和属性赋值的顺序总结一下:
属性赋值
如果Clz定义了__setattr__方法,那么调用该方法,否则
如果“attr”是出现在Clz或其基类的__dict__中, 且attr是data descriptor, 那么调用其__set__方法, 否则
等价调用obj.__dict__[‘attr’] = var
属性查找
实例查找通过命名空间链进行扫描,数据描述器的优先级最高,其次是实例变量、非数据描述器、类变量,最后是 __getattr__() (如果存在的话)。
点运算符的查找逻辑在 object.__getattribute__() 中。这里是一个等价的纯 Python 实现:
def object_getattribute(obj, name):
"Emulate PyObject_GenericGetAttr() in Objects/object.c"
null = object()
objtype = type(obj)
cls_var = getattr(objtype, name, null)
descr_get = getattr(type(cls_var), '__get__', null)
if descr_get is not null:
if (hasattr(type(cls_var), '__set__')
or hasattr(type(cls_var), '__delete__')):
return descr_get(cls_var, obj, objtype) # data descriptor
if hasattr(obj, '__dict__') and name in vars(obj):
return vars(obj)[name] # instance variable
if descr_get is not null:
return descr_get(cls_var, obj, objtype) # non-data descriptor
if cls_var is not null:
return cls_var # class variable
raise AttributeError(name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
有趣的是,属性查找不会直接调用 object.__getattribute__() ,点运算符和 getattr() 函数均通过辅助函数执行属性查找:
def getattr_hook(obj, name):
"Emulate slot_tp_getattr_hook() in Objects/typeobject.c"
try:
return obj.__getattribute__(name)
except AttributeError:
if not hasattr(type(obj), '__getattr__'):
raise
return type(obj).__getattr__(obj, name) # __getattr__
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
因此,如果 __getattr__() 存在,则只要 __getattribute__() 引发 AttributeError (直接引发异常或在描述符调用中引发都一样),就会调用它。
同时,如果用户直接调用 object.__getattribute__() ,则 __getattr__() 的钩子将被绕开。
set容器中获取头部元素的函数叫啥?
vector:Constructors 构造函数
Operators 对vector进行赋值或比较
assign() 对Vector中的元素赋值
at() 返回指定位置的元素
back() 返回最末一个元素
begin() 返回第一个元素的迭代器
capacity() 返回vector所能容纳的元素数量(在不重新分配内存的情况下)
clear() 清空所有元素
empty() 判断Vector是否为空(返回true时为空)
end() 返回最末元素的迭代器(译注:实指向最末元素的下一个位置)
erase() 删除指定元素
front() 返回第一个元素
get_allocator() 返回vector的内存分配器
insert() 插入元素到Vector中 参考技术A
set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。
set 默认是升序排列
也可以指定顺序
set<int,less<int> > setIntA; //该容器是按升序方式排列元素。
set<int,greater<int>> setIntB; //该容器是按降序方式排列元素。
获取头部元素用迭代器实现 int front = *setInt.begin();
比如
以上是关于python属性重新赋值叫啥的主要内容,如果未能解决你的问题,请参考以下文章