漫话 python 类的私有方法

前几天同事问我关于如何外部访问python私有属性的问题,当时觉得私有属性不应该被外部访问。但是回过头来想一下python私有属性的实现却是很有意思。
先看这段代码

class A(object):
    _a = "this is _a"
    __a = "this is __a"
    def set_private(self, a1, a2):
        self._a = a1
        self.__a = a2
 
    def read_private(self):
        print(self._a, self.__a, self._A__a)
 
a1 = A()
print(a1._a)
print(hasattr(a1.__a))  # => False; 没有__a这个属性
print(a1._A__a)  # 可以成功读取私有变量
a1._A__a = None
a1.read_private()  # 可以从外修改私有变量

貌似python对私有变量的实现就是改了下变量名啊?看着貌似挺简单的,但是聪明的你一定发现了问题,重命名变量如果重名咋办?
看这段例子:

class A(object):
    _a = "this is _a"
    __a = "this is __a"
    _A__a = "this is _A__a"
    def set_private(self, a1, a2=None, a3=None):
        self._a = a1
        if a2:
             self.__a = a2
        if a3:
            self._A__a = a3
    def read_private(self):
        print(self._a, self.__a, self._A__a)
 
a2 = A()
a2.read_private()  # 期望结果是 (this is _a, this is __a, this is _A__a)
#=> (this is _a, this is _A__a, this is _A__a)
## 说明python的私有变量果然只是简单测重命名,规则就是"_{class_name}__{private_name}"
a2.set_private(0, 1)
print(a2.read_private())
#=> 0 1 1
a2._A__a = 2
print(a2.read_private())
#=> 0 2 2

不难看出,类方法内修改__private_name 等价于外部直接访问"_{class_name}__{private_name}"
看似很美好,但如果出现重名的类呢?

class A(object):
  __p1 = 1
  __p = "A1"
  def get1(self):
     print(self.__p, self.__p1)
 
A1 = A
class A(object):
  __p = "A2"
  __p2 = "2"
  def get2(self):
     print(self.__p, self.__p2)
 
A2 = A
 
print(A1, A2)
class B(A1, A2):
   _A__p = "B"
 
b = B()
print(b.get1())
print(b.get2())

不难发现python私有变量只是一种访问的语法糖。在某些情况下,比如利用type函数动态生成的class中使用了相同的类名称和空间。一定会造成私有变量的互相影响,所以再大型项目中需要谨慎使用

发表在 python | 标签为 , | 留下评论

新的开始

忙啊忙,突然发现自己也成了瞎忙族。这两年想做很多事情,结果是很多事情没做成。本来想自己写一个blog系统,结果连写blog也荒废了。
花了两天的时间,吧荒废的网站重新上线,启用了新的域名,加上了https,wordpress部署到了docker里面,从美国机房迁移到英国机房。尝试吧老的数据导入回来。不得不说wordpress虽然有很多缺点,但却是好用。虽然坑也不少,但好歹能用,也没有什么好抱怨的了。比起打王者农药,玩blog虽然也浪费时间,起码更有意义吧。

发表在 日誌, 系统更新 | 留下评论

python 原类Metaclass 与 class语句

python 在2.7中新式类中出现了原类这个强大无比的工具,以下内容仅仅针对新式类

原类(metaclass)
内置的type是所有原类的基类,原类仅接受3个参数,原类的实例是一个普通类
普通类(cls)
内置的object是所有普通类的基类,可接受任意个参数,普通类的实例是一个
一般对象(obj)
由普通类创建的对象

type 是可接受多个参数的,若三个参数则返回一个cls,第一个参数表示函数名,第二个参数表示继承列表,第三个参数是函数属性

cls = type("class_name", (), {}) 
print isinstanse(cls, objects)  # -> True
print cls.__class__  # -> <type 'type'>  通过原类创建的都是新式类,都继承自object
print object.__class__  # -> <type 'type'> 默认的object对象是type的一个实例

再来说说python的class语句吧

class MyClass(object,):
    a = 1
    def addone(self, x):
        return self.a + x
基本上是等价于
def addone(self, x):
    return self.a + x
MyClass = type('MyClass', (object,), {'a': 1, 'addone': addone}
del addone
class 看起来就是一个type的语法糖,这是就有了一个问题;如果是继承自type得原类创建cls,是不是class语句就没法玩了。此时引出了一个python类的一个内置属性 __metaclass__ (__meta__ python>3)python规定如果一个class申明的时候设置了__metaclass__就用这个对应的值来作为type。听起来有点绕,看例子
class MyType(type):
    def __call__(self, *args, **kwargs):
        print "meta_calling", args, kwargs
        obj = super(MyType, self).__call__(*args, **kwargs)
        print "meta_called", obj
        return obj
 
def addone(self, x):
    return self.a + x
MyClass = MyType('MyClass', (object,), {'a': 1, 'addone': addone}
del addone
基本上是等价于
class MyType(type):
    def __call__(self, *args, **kwargs):
        print "meta_calling", args, kwargs
        obj = super(MyType, self).__call__(*args, **kwargs)
        print "meta_called", obj
        return obj
 
class MyClass(object,):
    __metaclass__ = MyType
    a = 1
    def addone(self, x):
        return self.a + x
这样看起来,class语句基本都可以被type可调用对象替代了,这可以干什么呢?总的说,就是我们不用再去手动申明class了,我们可以写一个程序用type自己创建class,毕竟在python这种动态语言中,class也是一个对象。
不知道各位看官是否写过python的web项目。在django或者flask中使用form表单控件时,通常都是申明一个表单类;如果表单的内容不确定的时候,比如表单内容根据配置来显示,这个时候用type自动生成对应的表单类就非常的简洁。
当然,原类是概念上的统一,起强大特性能不用就不要用(但不是说能不会就不会),用的好造福万年,用的不好遗臭万年
发表在 python | 标签为 , | 留下评论