Python 发现个奇怪知识,类属性不被实例化修改,即可被外部修改

2022-03-25 19:33:37 +08:00
 llsquaer

今天使用 selenium 使用拖拽效果,发现异常的慢.网上一顿搜索,发现解决办法是修改源码控制间隔的属性值..

这样太不友好了.直接全局改了.后来想看能不能外部写一个继承方法来做,看了源码后,还是放弃,水平有限.于是想的通过外部修改.

于是做了一下实验.

代码如下. 备注有返回值

class AAA():
    DEP = 0

    def get(self):
        return self.DEP


print(AAA.DEP)  # 0

AAA.DEP = 5  # 修改默认属性

ex_1 = AAA()  # 实例化 ex_1
print(ex_1.get())  # 5

AAA.DEP = 10  # 再次修改默认属性
print(ex_1.get())  # 10

ex_1.DEP = 15  # ex_1 修改实例属性
print(ex_1.get())  # 15

AAA.DEP = 12  # 再次修改默认属性
print(ex_1.get())  # 15  被上次实例化修改属性后,类属性不再被修改,即返回 15


ex_2 = AAA()  # 实例化 ex_2
print(ex_2.get())  # 12  上段 ex_2 实例化之前前修改的值

AAA.DEP = 20  # 修改原始属性
print(ex_2.get())  # 20

ex_2.DEP = 30
print(ex_2.get())

# 总结,类实例化后若原始属性不被修改 ,均可被外动态修改

总结,类实例化后若原始属性不被修改 ,均可被外动态修改

后来再次实验,如果被 init 初始化,值也不能被修改了

class AAA():
    DEP = 0
    def __init__(self):
        self.DEP=10   # 被 init 初始化后,不能被修改了
    
    def get(self):
        return self.DEP

感觉奇奇怪怪的知识增加了..

这么说,再次使用 selenium 不用使用 time.sleep 来控制了吧. 还没试,先分享下新发现.

3348 次点击
所在节点    Python
25 条回复
zagfai
2022-03-25 19:40:35 +08:00
Python 就是太灵活了,所以说做大型项目架构的严谨性更为重要
alphanow
2022-03-25 19:55:22 +08:00
建议读一读 python 官方教程中的 classes 一节。类变量和实例变量并不等价,类变量在所有实例中共享,在通过实例查找变量时实例变量优先于类变量被获取。

把类变量用实例变量覆盖的方式不如通过子类覆写类变量的方式安全。
Nitroethane
2022-03-25 19:59:59 +08:00
你是把类变量喝对象变量混淆了。看这个 https://stackoverflow.com/a/5690920
mangoDB
2022-03-25 20:18:12 +08:00
楼主看下 3# 答案,解释很清晰。
Dganzh
2022-03-25 20:36:04 +08:00
好像也没啥问题,实例属性覆盖类属性了。
主要是 Python 太灵活,ex_1.DEP = 15 ,这样可以动态创建实例属性。
ClericPy
2022-03-25 21:00:16 +08:00
省流一句话:
self.xxx 从对象作用于里找 xxx 属性 /方法, 没找到, 去类作用域里面找, 找到了; self.xxx 赋值以后, 从对象作用域里找到了, 就不去类作用域找了
rrfeng
2022-03-25 21:21:30 +08:00
上面解释很清晰了。但是可以在外面改类内的变量就很奇怪…
imn1
2022-03-25 21:22:10 +08:00
只会在同一个引用链条上起作用,分别两个地方引用,改变其中一个,另一个还是原值,相当于作用域不同

或者试试外部改变后,reload 一次看看

“总结,类实例化后若原始属性不被修改 ,均可被外动态修改”
这句就不评价了,前面几楼都讲太多了

AAA.DEP=30
a=AAA()
a.DEP=20
print(a.DEP)
print(AAA.DEP)
imn1
2022-03-25 21:30:22 +08:00
@rrfeng #7
因为 python 几乎全部变量都是引用,只有 int/float/complex/string 等少量类型不是
外部改变实际上只是改变了引用的指向,我是这样理解的,但也不知道对不对
反正我也不知道如何用原生语法定义一个不可变(只读)的常量
JeffGe
2022-03-25 21:43:59 +08:00
@imn1 不用几乎,Python 里面所有的变量赋值都是共享对象传递
https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing
beastk
2022-03-26 00:07:10 +08:00
和调用顺序有关,本来就是这样
lux182
2022-03-26 01:27:36 +08:00
个人感觉大型项目还是不敢用 python ,如此灵活的语法太容易写出致命 bug
LeeReamond
2022-03-26 02:35:40 +08:00
@lux182 解决方案:招募正经程序员
vicalloy
2022-03-26 07:51:40 +08:00
monkey patch 这 python 里面也算是挺常规的操作。
est
2022-03-26 09:19:00 +08:00
这还不算啥,你还可以在运行中改方法、改方法的参数,改方法内部的变量。
mongodb
2022-03-26 09:32:53 +08:00
Python 太灵活是真的,但拜托你们也把文档读完啊……
llsquaer
2022-03-26 11:18:02 +08:00
@est 之前也干过运行中从外部改方法..但是改的时候都是实例化后修改的实例方法.. 现在是突然想到直接改类默认属性的想法..所以才实验一番..
其实 2 楼 3 楼已经说明了原因.. 只是忘记了在类变量内部搜索方式了..因为平时很少这么用的场景..
实际上实例化后也是可以获取到初始属性的 使用 __class__ 方法
llsquaer
2022-03-26 11:32:42 +08:00
@alphanow
@Nitroethane

是的哈..一说就突然想起来了 哈哈
kilasuelika
2022-03-27 10:29:49 +08:00
我寻思这其实有点像 C++中的静态变量?同样可以在没有实例化的情况下修改:
```
#include <iostream>

struct AAA {
static int DEP;

int get()const {
return this->DEP;
};
};
int AAA::DEP = 0;
int main()
{
AAA::DEP = 5;
std::cout << AAA::DEP << std::endl; //5

AAA a;
std::cout << a.get() << std::endl; //5

a.DEP = 15;
std::cout << a.get() << std::endl; //15
}
```
不过有个区别是`this->DEP`与`DEP`是同一个变量,所以这里在 C++中是不行的:
```
AAA.DEP = 12 # 再次修改默认属性
print(ex_1.get()) # 15 被上次实例化修改属性后,类属性不再被修改,即返回 15
```
C++里面后一句必然是 12 。
DOLLOR
2022-03-27 15:29:11 +08:00
@kilasuelika
我写 python 从来不用 class ,也是觉得不可思议。
python 的 self.xxx ,有可能指向静态成员(类变量),也可能指向实例成员(对象变量)。
如果实例化的对象时没有初始化实例成员,就会使用静态成员作为默认值。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/842921

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX