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
InFaNg
V2EX  ›  Python

关于 Class 中私有变量的一个小问题

  •  
  •   InFaNg · 2017-08-15 21:17:51 +08:00 via iPhone · 1701 次点击
    这是一个创建于 2444 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果 Python 中的内部属性不希望被外部访问,那么就应该在属性名称前加两个下划线。这样,就无法在外部直接访问了。如下:

    class Student(object):
    
        def __init__(self, score):
            self.__score = score
    

    之后,我们如果直接想通过属性名称调用,就需要使用@property了:

    @property
    def score(self):
            return self._score
    

    如果这样,要增加不少代码,而调用实例名称.score的结果与不加下划线的结果一致。那么,加两个下划线的目的是什么呢

    10 条回复    2017-08-16 12:01:29 +08:00
    congeec
        1
    congeec  
       2017-08-16 00:37:10 +08:00 via iPhone   ❤️ 1
    加下划线就是为了让变量 private, 然而你又想在外部访问 private 变量。这让大家很为难呐🤷‍♂️
    wisej
        2
    wisej  
       2017-08-16 00:49:05 +08:00 via Android   ❤️ 1
    加下划线一般意味着这个变量是私有的,也就是希望你不要去修改它。
    但是,你只加下划线的话,虽然无法直接访问,但是你想的话还是可以访问并修改的!(也就是说,下划线只是一个大家的一个共识,它只对 responsible user 起作用)
    但你加上第二段代码后,它内部通过__setattr__方法会拦截你对这个私有变量的修改。也就是从代码层面防止你动手脚了。
    这就是两者区别,相当于一个道德约束,一个是法律约束
    iEverX
        3
    iEverX  
       2017-08-16 00:51:27 +08:00 via Android   ❤️ 1
    不能修改
    billwsy
        4
    billwsy  
       2017-08-16 01:08:43 +08:00 via iPhone   ❤️ 1
    @wisej __setattr__并没有阻止对_score 的访问,而是模拟了对 score 的访问,尽管它并不存在

    加一个下划线是约定,也就是外部的代码不应该去访问_score ;加两个下划线是强制,外部的代码不能去访问__score (尽管根据混淆规则还是有办法去访问,但是更不直观)

    回到问题,加两个下划线的目的是防止外部代码去访问__score,任何对它的读写应该通过成员函数来完成。好处是读写的时候你可以做更多的判断,例如写的时候判断分数是不是在一个范围内,读的时候做校验之类的。而 property 利用 Python 的机制(也就是上面说的__setattr__)实现了更直观的语法,你可以通过 print self.score 来调用 self.score()函数,而它正巧返回了 self.__score 这个成员变量。
    billwsy
        5
    billwsy  
       2017-08-16 01:15:38 +08:00 via iPhone   ❤️ 1
    纠错…上面说的 Python 机制并不是__setattr__,而是 property 对象的__get__
    lxml
        6
    lxml  
       2017-08-16 05:19:05 +08:00   ❤️ 1
    @wisej #2 set 的作用不应该是拦截,而是类似于装饰器一样的,对传入的值做一些过滤,类型验证是否合法等工作,没问题就放行。
    wisej
        7
    wisej  
       2017-08-16 11:57:23 +08:00   ❤️ 1
    @lxml @billwsy 嗯。。我错了 @property 内部实现机制是 Descriptor (我一开始以为是用__getattribute__,__setattribute__来实现的... ,但是这样会影响到其他属性访问)

    我贴一下我现在的理解,你们看对不,不对望指正:

    score = property(fget, fset, fdel, docs),会生成一个名为 score 的 Descriptor.这样,当我们访问
    Student.score 等于 Student.score.__get__ 。而 __get__ 内部调用了函数 fget。

    至于
    ```
    @property
    def score(self):
    return self._score
    @score.setter
    def score(self):
    pass
    ```
    则是分别调用了 property 的 getter、setter 将对应的函数引用赋值给 fget 和 fset
    zhusimaji
        8
    zhusimaji  
       2017-08-16 12:00:59 +08:00   ❤️ 1
    1、外部实例对象想访问对象成员变量应该是通过 getsatt 方式获取属性
    2、楼主使用 @property 修饰 score 函数使其成为属性
    3、在外部调用的时候会经历 1、2 两个步骤
    4、两个下划线就是私有变量,希望你不要从外部访问,但是你仍然是可以从外部通过某个手段访问,楼主可以试试,没有绝对的私有,只是你这么定义就是遵守约定不要乱来
    zhusimaji
        10
    zhusimaji  
       2017-08-16 12:01:29 +08:00   ❤️ 1
    打错了,getattr
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1104 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 18:52 · PVG 02:52 · LAX 11:52 · JFK 14:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.