java 调用python_[Python]Python中的私有变量

bea4fb017d8651329dc1ff53b4a35950.png

什么是private

StackOverflow上有一个备受关注的问题,想必也是很多刚从Java等支持面向对象范式的编程语言转来的同学想问的:

Python的类是否可以定义私有变量(private variable)?

有兴趣的同学可以直接翻阅一下原问题以及大神们的回答

https:// stackoverflow.com/quest ions/1641219/does-python-have-private-variables-in-classes https:// stackoverflow.com/quest ions/70528/why-are-pythons-private-methods-not-actually-private

私有变量的概念要从面向对象中的封装谈起。我们定义一个类/对象,本质上是在描述这个类/对象的状态行为,其中状态成员变量表示,行为成员方法表示。在这些状态和行为中,有些我们需要公开作为外界调用的接口,有些我们需要隐藏作为内部具体的实现。

举个例子说明一下,加入我们要定义一个Lady类,用于描述一位女士,那么表示女士年龄的状态变量age就需要设置成私有变量,调用者不能轻易访问,不过,可以定义一个

在Java中,在成员变量和成员方法的声明前加上private修饰符,即可使得该变量(方法)成为私有变量(方法),一旦声明为private,则该变量(方法)对于外部调用者不可访问

我们今天要讨论的问题是,该封装机制在Python中是怎样的实现的。

不存在的

Python中没有禁止访问类中某一成员的保护机制。Java是一门非常工程化的语言,其哲学就是为工程服务,通过各种限制,尽可能减少程序员编写错误代码的机会。而Python则相反,其哲学为信任编码者,给程序员最少的限制,但程序员必须为自己编写的代码负责

那是否意味着Python认为面向对象编程不需要封装呢?答案也是否定的。Python通过编码规范而不是语言机制来完成封装,具体而言,Python规定了对变量命名的公约,约定什么样的变量名表示变量是私有的,不应该被访问(而不是不能被访问)。

根据PEP 8的建议:

https://www. python.org/dev/peps/pep -0008/#designing-for-inheritance
Always decide whether a class's methods and instance variables (collectively: "attributes") should be public or non-public. If in doubt, choose non-public; it's easier to make it public later than to make a public attribute non-public.
...
We don't use the term "private" here, since no attribute is really private in Python (without a generally unnecessary amount of work).

在设计类时,一定要明确类的成员变量和方法是否公开,如果暂不确定,则不公开,因为把一个私有变量修改为公开变量要比把一个公开变量修改为私有变量更简单。但是Python中一般不用private这个属于,因为Python里没有真正的private机制。

官方文档进一步解释了我们应该怎样通过编码规范来实现封装:

https:// docs.python.org/3/tutor ial/classes.html#private-variables
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.

在Python中,没有绝对在外部访问不了的变量,但Python代码应遵循一个规范:以单个下划线开头的变量或方法应被视为非公开的API,因此不用特别声明,外部的调用者也不应该去访问以单下划线开头的变量或方法,因为类的设计者也遵循着这个规范,他会默认外部的调用者不会访问这种变量。

另外,Python通过一个非常简单的机制完成了一个伪私有化功能,这个机制名叫名称转写(name mangling)以双下划线开头,并以最多一个下划线结尾的标识符,例如__X,会被转写为_classname__X,其中classname为类名。这个机制实现起来非常简单,而且很大程度避免了调用者的误访问,但并不能像Java的private限定符那样完全杜绝外部的访问。我们以代码直观展示:

 class A():
     def __init__(self):
         self.__private_var = 7
 ​
 >>> a = A()
 >>> print(a.__private_var)
 AttributeError:
     'A' object has no attribute '__private_var'
 >>> print(a._A__private_var)
 7

需要注意的是,Python的魔术方法,比如__len__, __bool__等不受此限制,因为它们均以双下划线结尾

 class B():
     def __len__(self):
         return 7
 >>> b = B()
 >>> print(b.__len__())
 >>> 7

为什么需要名称转写

既然单下划线开头的变量名约定足以提醒调用者这是一个非公开变量,为什么还需要双下划线开头的变量进行名称转写呢?这涉及到面向对象中另一个重要概念,继承

在Java中,声明为private的成员不会从父类被继承到子类,而Python中没有这样的强制机制。而进行名称转写则能有效避免子类中方法的命名冲突。

举一个具体的例子,我们知道,定义子类的时候,经常会调用父类的__init__()方法,假如父类的__init__()方法调用了父类的非公开函数__initialize(),当我们在子类中也需要__initialize()函数时会造成父类__init__()的异常行为,而名称转写避免了这一冲突,父类的__init__()实际上调用的是_父类名_initialize()

总结

Python中不存在只能从对象内部访问的“私有(private)”变量,但Python制定了以下公约:

  • 以单下划线开头的成员变量或方法,是设计者不想暴露给外部的API,使用者不应该进行访问。
  • 以双下划线开头且以至多一个下划线结尾的成员变量或方法,会进行名称转写。

至于Python为什么通过这样不够强力的手段进行约束,这源于Python这门语言的文化和哲学:简单至上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值