V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
llsquaer
V2EX  ›  Python

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

  •  
  •   llsquaer · 92 天前 · 2711 次点击
    这是一个创建于 92 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天使用 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 来控制了吧. 还没试,先分享下新发现.

    24 条回复    2022-03-28 10:04:23 +08:00
    zagfai
        1
    zagfai  
       92 天前
    Python 就是太灵活了,所以说做大型项目架构的严谨性更为重要
    alphanow
        2
    alphanow  
       92 天前   ❤️ 1
    建议读一读 python 官方教程中的 classes 一节。类变量和实例变量并不等价,类变量在所有实例中共享,在通过实例查找变量时实例变量优先于类变量被获取。

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

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

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

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

    是的哈..一说就突然想起来了 哈哈
    kilasuelika
        19
    kilasuelika  
       90 天前
    我寻思这其实有点像 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
        20
    DOLLOR  
       90 天前
    @kilasuelika
    我写 python 从来不用 class ,也是觉得不可思议。
    python 的 self.xxx ,有可能指向静态成员(类变量),也可能指向实例成员(对象变量)。
    如果实例化的对象时没有初始化实例成员,就会使用静态成员作为默认值。
    kilasuelika
        21
    kilasuelika  
       89 天前 via Android
    @DOLLOR 我也是第一次听说 python 中的这种情况。感觉这个特性有点容易导致问题🤣
    neoblackcap
        22
    neoblackcap  
       89 天前
    如果多写 Python 就会习惯了,这设计就是这样的。甚至还有用这个特性去替代单例模式。
    lanlanye
        23
    lanlanye  
       89 天前 via iPhone
    所以不建议水平不够的人写 Python ,更不建议和水平不够的人合作 Python 项目
    语言没有约束,就只有靠程序员自己去约束了
    hhhhhh123
        24
    hhhhhh123  
       89 天前
    我没太看懂你想要啥 你可以试试这个
    class AAA:
    DEP = 0

    def get(self):
    return self.DEP

    def set(self, x):
    return self.DEP + x


    print(AAA().set(12)) # 12
    print(AAA().get()) # 0
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2572 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:17 · PVG 22:17 · LAX 07:17 · JFK 10:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.