玩转Python|类与方法的隐藏秘密(2)

本文介绍了如何手动创建Python对象和类,解析了私有字段的本质。通过示例展示了手动制造对象的过程,以及利用`type`构造类的三个阶段。此外,文章探讨了Python中私有字段并非严格私有,而是依赖于开发者的约束,与Java等静态语言的私有字段机制不同。
摘要由CSDN通过智能技术生成

上期Python专栏,为大家介绍了Python对象是如何被构造的,以及类与对象的本质。大家有没有觉得收获满满呢?戳这里可以复习下。

今天来给大家讲讲新的硬核知识,经常看Python中文教程的朋友一定会发现,我们讲的新内容其实是现有Python中文教程里不常见的哦。

如何手动制造一个对象?

基于上期的分析,类和对象的本质已经初见端倪——类和对象本质上也是一种映射结构,这一结构中存值的那一部分位于 __dict__ ,而存储业务逻辑的部分则是各个函数,它们在 dir(t) 中均可以找到名称,并且可以通过 getattr 进行访问(实际上在Python中,函数也同样是一个对象)。

因此,我们可以基于上述的原理,尝试构造一个简易的对象出来。例如下面的例子:

class MyObject(object):
    pass


if __name__ == '__main__':
    t = MyObject()  # the same as __new__
    t.x = 2  # the same as __init__
    t.y = 5


    def plus(z):
        return t.x + t.y + z


    t.plus = plus  # the same as function def

    print(t.x, t.y)
    print(t.plus(233))

首先在第6行,我们模仿 __new__ 方法的思路,手动创建一个空对象(注意不能直接用 object ,而需要继承一层,具体原因详见官方文档中的Note部分);

接下来分别对对象的属性进行赋值,包括数值 x 和 y ,以及一个会基于 t.x 和 t.y 进行运算处理的函数 plus (一般我们更习惯于称之为方法);


最后就是使用这一手动创建的对象,可以看到 t.x和t.y均可正常使用,并且方法t.plus(z)也可以被正常调用。经过这一系列操作,一个手工创建的对象就产生了,而且从使用者的角度来看,也和正常实例化的对象并无差异

如何手动制造一个类?

不仅对象,类也是可以手动制造出来的。话不多说,我们先看看来自官方文档的构造 type 类说明

 

看起来挺长,不过后续附了一个最为简明扼要的例子。

# first code
class X:
    a = 1

# second code, the same as the former one
X = type('X', (), dict(a=1))

​​​​​​​

所以其实依然不难理解,简单来说就是三个基本参数:

  • 名称( name )——字面意思,表示构造的类名
     

  • 基类( bases )——字面意思,表示所需要继承的基类

  • 字典( dict )——即需要赋予对象的属性

因此基于以上的原理,我们可以构造出来一个自己的类,就像这样:

def __init__(self, x, y):
    self.x = x
    self.y = y


def plus(self, z):
    return self.x + self.y + z


XYTuple = type('XYTuple', (), dict(
    __init__=__init__,
    plus=plus,
))

if __name__ == '__main__':
    t = XYTuple(2, 5)
    print(t.x, t.y)
    print(t.plus(233))

# 2 5
# 240

# The definition of class is exactly the same as :
# class XYTuple:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
# 
#     def plus(self, z):
#         return self.x + self.y + z

不难发现,从这样的视角来看,一个类的装配也大致分为三步:

  • “初始化阶段”——此阶段会创建一个指定名称的类对象
     

  • “继承阶段”——此阶段会尝试在类对象上建立与已有类的继承关系
     

  • “装配阶段”——次阶段会将类所需的各个属性,装配至类对象上

至此,经过了三个阶段后,一个类对象创建完毕,并且在使用上和正常定义的类并无差别。

私有字段的本质?

对于了解Python面向对象或学习过Java、C++等其他语言的读者,应该对私有字段这个东西并不陌生(如果还不够了解的话可以看看Python3 面向对象 - 类的私有属性)。

Python3面向对象-类的私有属性参考地址:

https://www.runoob.com/python3/python3-class.html 

在Python中,我们所熟知的私有字段大致是如下的形态:

class T:
    def __init__(self):
        self.__private = 1   # private field, starts with __
        self._protected = 2  # protected field, starts with _
        self.public = 3      # public field, starts with alphabets

简单来说就是:

  • 私有字段,仅可以被类内部访问,以双下划线开头
     

  • 保护字段,可以被当前类及其子类访问,以单下划线开头
     

  • 公有字段,可以被自由访问,以字符开头

因此对上面的例子中,实际访问效果如下:

t = T()
t.__private   # Attribute Error!
t._protected  # 2
t.public      # 3

保护字段和公有字段是可以被访问到的,但是一般情况下,保护字段并不推荐在当前类或子类以外的地方进行访问(实际上当你这么做的时候,不少IDE都会报出明确的warning),而私有字段则无法访问,直接访问会导致报错。看起来似乎一切很正常,但是让我们来看看上面例子中变量 t 内部都有什么:

t.__dict__  # {'_T__private': 1, '_protected': 2, 'public': 3}

其中 public 和 _protected 是意料之内的,但是除此之外还包含一个_T__private,并且其值正是在构造函数中所赋予的值。基于这一点,我们再来做个实验。

t._T__private  # 1

发现私有字段居然也可以被访问。至此,我们可以得出一个结论——在Python中,并不存在严格意义上的私有字段,我们所知道的私有字段本质上更像一种语法糖效果,而保护字段则干脆是被摆在明面上的。

从这个角度来看不难发现,在Python中这些字段之所以还能起到私有字段或保护字段应有的效果,本质上靠的是开发者意义上的约束,而非语言系统本身的强制力。这一点和Java等静态语言存在本质上的差异,在Java中定义的私有字段一般无法通过正常途径进行访问,即便通过反射机制强制读取,也需要绕开一系列机制。

🥳 期预告

本文重点针对类的特性,从原理角度进行了分析。在本系列的下一篇中,会重点针对类的方法和属性进行讲解,以及treevalue第三弹也将会在不久后推出,敬请期待


👏:欢迎大家体验OpenDILab开源的项目icon-default.png?t=M4ADhttps://github.com/opendilab

🪐:作者小哥的开源项目(部分仍在开发中)

命令行工具:https://github.com/HansBug/plantumlcli

对象转可执行代码:https://github.com/HansBug/potc

好用的工具库:https://github.com/HansBug/hbutils​​​​​​​

扫码即可了解更多开源信息~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值