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

2017-08-15 21:17:51 +08:00
 InFaNg

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

class Student(object):

    def __init__(self, score):
        self.__score = score

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

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

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

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

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

回到问题,加两个下划线的目的是防止外部代码去访问__score,任何对它的读写应该通过成员函数来完成。好处是读写的时候你可以做更多的判断,例如写的时候判断分数是不是在一个范围内,读的时候做校验之类的。而 property 利用 Python 的机制(也就是上面说的__setattr__)实现了更直观的语法,你可以通过 print self.score 来调用 self.score()函数,而它正巧返回了 self.__score 这个成员变量。
billwsy
2017-08-16 01:15:38 +08:00
纠错…上面说的 Python 机制并不是__setattr__,而是 property 对象的__get__
lxml
2017-08-16 05:19:05 +08:00
@wisej #2 set 的作用不应该是拦截,而是类似于装饰器一样的,对传入的值做一些过滤,类型验证是否合法等工作,没问题就放行。
wisej
2017-08-16 11:57:23 +08:00
@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
2017-08-16 12:00:59 +08:00
1、外部实例对象想访问对象成员变量应该是通过 getsatt 方式获取属性
2、楼主使用 @property 修饰 score 函数使其成为属性
3、在外部调用的时候会经历 1、2 两个步骤
4、两个下划线就是私有变量,希望你不要从外部访问,但是你仍然是可以从外部通过某个手段访问,楼主可以试试,没有绝对的私有,只是你这么定义就是遵守约定不要乱来
wisej
2017-08-16 12:01:27 +08:00
zhusimaji
2017-08-16 12:01:29 +08:00
打错了,getattr

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

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

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

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

© 2021 V2EX