在这篇文章中,将大开脑洞,讨论不同的Python对象“创建”的方法。通过比较不同的实现方法,将会揭露很多被大家忽略的Python语言高级知识。准确来说,本篇文章创建对象的方法并不是真正的“创建”,而是通过不同的方式获取到类对象,然后进行调用创建对象。但是,本篇文章的讨论对于绝大多数读者来说依然非常有意义,可以接触到很多比较隐蔽的Python知识。
1 问题
在这篇文章中,我们会用到下面这个名为Point的类,这是一个最简单也最常见的类。
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def new_object(cls, x, y):
return cls(x, y)
现在有一个问题,对于这个类定义,你能想到多少种创建对象的方法?
读者先不要看答案,在脑海里面想一下,看看自己能够想到多少种创建对象的方法。
笔者可以明确的告诉大家,有10种不同的方法。很多时候并不是因为我漏掉了什么重要的知识,而是因为我们思维定式,没办法打开脑洞。
2 答案
为了便于读者测试与学习,这里直接给出可以运行的代码,以便大家进行测试和验证。
from __future__ import print_function
import sys
import copy
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def new_object(cls, x, y):
return cls(x, y)
def __str__(self):
return "{0}({1}, {2})".format(self.__class__.__name__, self.x, self.y)
def make_object(Class, *args, **kwargs):
return Class(*args, **kwargs)
def main():
point1 = Point(1, 2)
point2 = eval("{}({}, {})".format("Point", 2, 4))
point3 = getattr(sys.modules[__name__], "Point")(3, 6)
point4 = globals()["Point"](4, 8)
point5 = make_object(Point, 5, 10)
point6 = copy.deepcopy(point5)
point6.x = 6
point6.y = 12
point7 = point1.__class__(7, 14)
point8 = Point.new_object(8, 16)
point9 = type(point1)(9, 18)
point10 = Point.new_object.__self__(10, 20)
for i in range(1, 11):
name = "point{0}".format(i)
print(locals()[name])
if __name__ == '__main__':
main()
运行上面的代码:
~$ python create_python_object.py
Point(1, 2)
Point(2, 4)
Point(3, 6)
Point(4, 8)
Point(5, 10)
Point(6, 12)
Point(7, 14)
Point(8, 16)
Point(9, 18)
Point(10, 20)
3 解释说明
下面是对创建对象的10种方法的解释说明:
- point1是按照传统的方式创建的,也是大家最常用的方式。这种方式也称之为静态方式,其他方式称之为动态方式。
- 在创建point2、point3与point4时,我们把类名当做是普通的参数传递给相应的函数。point2调用了存在安全隐患的eval函数。在生产环境的程序中,千万不要使用这个函数。
- point3与point4原理是一样的。在point3中,先通过
sys.modules[__name__]
获取当前模块的所有属性。在point4的创建中,直接使用globals函数获取当前模块中的属性。获取模块的所有属性以后,可以直接通过类的名称获取到Point这个类对象。获取到类对象以后,执行类调用,就创建了一个对象。 - point5通过调用make_object函数创建对象。在这个例子中,我们将类作为一个普通的参数,传递给make_object函数。
- 在创建point6时,我们通过copy.deepcopy函数复制了一个已经存在的对象,并通过修改对象属性的方式获得了一个新的对象。
- point7涉及到的知识点是,我们可以通过
object.__class__
获取到对象的类。获取到类以后,再执行类调用,就又创建了一个新的对象。 - point8也是比较容易想到的方式,point8是通过Python的类方法调用来创建一个对象。在类方法调用中,Python会自动将类传递给方法的第一个参数。
- point9使用了Python内置的type函数,该函数并不是通过一个字符串返回变量的类型,而是返回一个“类对象”,类对象可以直接进行调用。
- point10涉及到的知识点是,可以通过classmethod的
__self__
获取到classmethod的类,获取到类以后,再执行类调用,就又创建了一个新的对象。
4 结束语
在我们编写程序时,总是应该使用最简单的方式来创建类(第一种方法)。但是,这里讨论的创建对象的方法也不是没有用处。这里的这个例子,涉及到了模块、对象拷贝、globals函数、类方法、__class__
属性等知识点。对于这段程序,如果大家想不到这么多创建对象的方法,完全不是问题。但是,如果大家不看解释说明,不能够看懂所有创建对象的方式,则说明对Python掌握还有遗漏。从这个角度来说,讨论创建Python对象的不同方法还是非常意义的,可以对Python知识进行查漏补缺。
作者介绍
赖明星,架构师、作家。现就职于腾讯,参与并主导下一代金融级数据库平台研发。有多年的 Python 开发经验和一线互联网实战经验,擅长 C、Python、Java、MySQL、Linux 等主流技术。国内知名的 Python 技术专家和 Python 技术的积极推广者,著有《Python Linux 系统管理与自动化运维》一书。