作者 | Radek Fabisiak
译者 | 弯月,责编 | 郭芮
出品 | CSDN(ID:CSDNnews)

以下为译文:
Python支持多种类型的编程范式,例如过程式编程、函数式编程、面向工具编程,而且还可以领悟多种类型的范式。
现如今面向工具编程的利用非常广泛。面向工具编程的基本元素是工具,其包含的数据成员称为属性,函数(例程、过程)称为方法。
工具是类的实例。换句话说,类紧张定义工具的构造,然后我们以类为模板创建工具。类不但包含方法定义,而且还包含所有实例共享的数据。
本文我们来磋商一下Python中的面向工具编程。我们将演示如何创建类,并利用类来实例化工具。本文的紧张内容如下:
创建Python类
数据属性
实例方法
属性
类和静态方法
继续
本文无法涵盖这些主题的所有详细信息。Python中的面向工具编程还包含其他很多方面。希望本文能够为你学习Python及实现面向工具供应一个良好的开端。
创建Python类
我们可以利用关键字class定义Python类,关键字后面紧跟类的名称、分号和类的实现:
>>> class MyClass:... pass...
按照老例,Python类的命名采取首字母大写(即PascalCase)。
现在让我们创建这个新类的一个实例,名为MyClass:
>>> a = MyClass>>> a<__main__.MyClass object at 0x7f32ef3deb70>
语句a = MyClass创建了MyClass的一个实例,并将它的引用赋值给变量a。
我们可以通过Python内置的函数type或直接通过属性.__class__来获取类型(即工具的类)。在拿到类(类型)之后,我们就可以利用属性.__ name__获取类的名字:
>>> type(a)<class '__main__.MyClass'>>>> a.__class__<class '__main__.MyClass'>>>> a.__class__.__name__'MyClass'
顺便提一句,Python类也是工具。它们是type的实例:
>>> type(MyClass)<class 'type'>
下面,我们来定义一个方法。
Python中每个实例方法的第一个参数必须对应于该实例,即该工具本身。按照老例,这个参数名为self。后面是其他参数(如果有须要的话)。在调用方法时,我们无需明确供应与参数self相对应的参数。
常日,我们须要定义的一个最主要的方法是.__init__。在类的实例创建后就会调用这个方法。该方法卖力初始化类成员。我们定义的.__init__如下:
>>> class MyClass:... def __init__(self, arg_1, arg_2, arg_3):... print(f'an instance of {type(self).__name__} created')... print(f'arg_1: {arg_1}, arg_2: {arg_2}, arg_3: {arg_3}')...
下面,我们来创建一个MyClass实例,看看这个初始化方法的详细事情。我们的.__init__方法须要三个参数(arg_1、arg_2和arg_3),记住我们不须要通报与self对应的第一个参数。以是,在实例化工具时,我们须要通报三个参数:
>>> a = MyClass(2, 4, 8)an instance of MyClass createdarg_1: 2, arg_2: 4, arg_3: 8
上述声明产生的结果如下:
创建一个MyClass类型的工具的实例。
自动调用该实例的方法.__init__。
我们通报给MyClass方法的参数:(2,4和8)会被通报给.__init__。
.__init__实行我们的要求,并输出结果。它利用type(self).__name__获取类的名称。
现在我们得到了一个类,它有一个方法.__init__,以及这个类的一个实例。
数据属性
下面我们来修正MyClass,增加一些数据属性。
我们利用.__init__初始化和定义了实例,我们还可以在这个方法或其他实例方法中,通过给某个数据属性赋值的办法改变属性值:
>>> class MyClass:... def__init__(self, arg_1, arg_2, arg_3):... self.x = arg_1... self._y = arg_2... self.__z = arg_3...
现在MyClass有三个数据属性:
.x可以获取arg_1的值
._y可以获取arg_2的值
.__ z可以获取arg_3的值
我们可以利用Python的解包机制,用更紧凑的形式编写这段代码:
>>> class MyClass:... def__init__(self, arg_1, arg_2, arg_3):... self.x, self._y, self.__z = arg_1, arg_2, arg_3...
属性名称中的下划线(_)是为了表明这些属性是“私有”属性:
开头没有下划线的属性(比如.x)常日可供工具外部的调用和修正。
开头拥有一个下划线的属性(比如._y)常日也可以从工具外部调用和修正。然而,下划线是一种惯用的标志,即该类的创建者强烈建议不要利用该变量。该当仅通过类的功能成员(比如方法和属性)调用和修正该变量。
开头拥有双下划线的属性(比如.__ z)将在名字润色过程中被改名(在本例中它将被改名为._MyClass__z)。你也可以通过这个新名称从工具外部调用和修正它们。但是,我强烈反对这种做法。该当尽通过类的功能成员以其原始名称进行调用和修正。
Python工具的数据属性常日存储在名为.__ dict__的字典中,它也是工具的属性之一。但是,你也可以将数据属性存储在其他地方。我们可以直接访问__dict__,或利用Python的内置函数vars获取.__ dict__:
>>> a = MyClass(2, 4, 8)>>> vars(a){'x': 2, '_y': 4, '_MyClass__z': 8}>>> a.__dict__{'x': 2, '_y': 4, '_MyClass__z': 8}
名字润色过程把键'__z'变成了'_MyClass__z'。
我们可以把.__ dict__当成普通的Python字典利用。
获取和修正与数据属性关联的值的常规方法如下:
>>> a.x2>>> a._y4>>> a.__zTraceback (most recent call last):File \"大众<stdin>\"大众, line 1, in <module>AttributeError: 'MyClass' object has no attribute '__z'>>> a.x = 16>>> a.x16>>> vars(a){'x': 16, '_y': 4, '_MyClass__z': 8}
请把稳,我们无法访问.__ z,由于.__ dict__没有键'__z'。
实例方法
下面,我们来创建两个实例方法:
●.set_z:修正.__ z。
●.get_z:返回.__ z的值。
请记住,每个实例方法的第一个参数(按照约定名为self)引用工具本身,但我们无需在调用方法时指定这个参数:
>>> class MyClass:... def__init__(self, arg_1, arg_2, arg_3):... self.x, self._y, self.__z = arg_1, arg_2, arg_3...... defset_z(self, value):... self.__z = value...... defget_z(self):... return self.__z...>>> b = MyClass(2, 4, 8)
方法.get_z和.set_z供应了传统的检索和修正.__ z值的方法:
>>> b.get_z8>>> b.set_z(16)>>> vars(b){'x': 2, '_y': 4, '_MyClass__z': 16}
你也可以在.get_z和.set_z中添加其他功能,例如检讨数据的有效性。这种方法实现了面向工具编程中的一个紧张观点:封装。
属性
还有一种方法(一种更Python的办法)访问和修正数据属性是利用属性。属性封装了一系列方法:getter、setter和deleter,但其行为与普通的数据属性相同。
下面的代码实现了属性.z,个中还包含.get_z和.set_z的功能:
>>> class MyClass:... def__init__(self, arg_1, arg_2, arg_3):... self.x, self._y, self.__z = arg_1, arg_2, arg_3...... @property... defz(self):... return self.__z...... @z.setter... defz(self, value):... self.__z = value...>>> b = MyClass(2, 4, 8)
如下,我们利用相应的属性.z来访问和修正数据属性.__ z:
>>> b.z8>>> b.z = 16>>> vars(b){'x': 2, '_y': 4, '_MyClass__z': 16}
这段代码比上述示例更精简优雅。
类与静态方法
除了实例方法和属性之外,类还可以拥有类方法和静态方法。
下面让我们为MyClass添加三个方法:
>>> class MyClass:... def __init__(self, arg_1, arg_2, arg_3):... self.x, self._y, self.__z = arg_1, arg_2, arg_3...... def f(self, arg):... print('instance method f called')... print(f'instance: {self}')... print(f'instance attributes:\n{vars(self)}')... print(f'class: {type(self)}')... print(f'arg: {arg}')...... @classmethod... def g(cls, arg):... print('class method g called')... print(f'cls: {cls}')... print(f'arg: {arg}')...... @staticmethod... def h(arg):... print('static method h called')... print(f'arg: {arg}')...>>> c = MyClass(2, 4, 8)
方法.f是一个实例方法。实例方法的第一个参数是工具本身的引用。这些方法可以利用self访问工具,利用vars(self)或self.__dict__访问工具的数据属性,还可以利用type(self)或self.__class__访问工具对应的类,而且它们还可以拥有自己的参数。
方法.g的开头包含润色器@classmethod,表明这是一个类方法。每个类方法的第一个参数都会指向类本身,按照约定该参数名为cls。与实例方法的情形一样,我们不须要明确供应与cls对应的参数。而类方法可以利用cls和自己的参数访问类本身。
方法.h的开头包含润色器@staticmethod,表明这是一个静态方法。静态方法只能访问自己的参数。
Python中常见的调用实例方法的方法如下:
>>> c.f('my-argument')instance method f calledinstance: <__main__.MyClass object at 0x7f32ef3def98>instance attributes:{'x': 2, '_y': 4, '_MyClass__z': 8}class: <class '__main__.MyClass'>arg: my-argument
常日,我们该当直接通过类(而不是实例)调用类方法和静态方法:
>>> MyClass.g('my-argument')class method gcalledcls: <class '__main__.MyClass'>arg: my-argument>>> MyClass.h('my-argument')static method h calledarg: my-argument
请记住,我们不须要通报类方法的第一个参数:与cls相对应的参数。
但是,我们可以像下面这样调用类方法和静态方法:
>>> c.g('my-argument')class method gcalledcls: <class '__main__.MyClass'>arg: my-argument>>> c.h('my-argument')static method h calledarg: my-argument
当我们调用c.g或c.h,但实例成员没有这样的名称时,Python会搜索类和静态成员。
继续
继续是面向工具编程的另一个主要特性。在这个观点中,类(称为子类或派生类)会继续其他类(称为超类或基类)的数据和函数成员。
在Python中,所有类都会默认继续Python自带的类工具。但是,我们可以根据须要定义得当的类继续层次构造。
例如,我们可以创建一个名为MyOtherClass的新类,该类继续了MyClass:
>>> class MyOtherClass(MyClass):... def __init__(self, u, v, w, x, y, z):... super.__init__(x, y, z)... self.__u, self.__v, self.__w = u, v, w...... def f_(self, arg):... print('instance method f_ called')... print(f'instance: {self}')... print(f'instance attributes:\n{vars(self)}')... print(f'class: {type(self)}')... print(f'arg: {arg}')...>>> d = MyOtherClass(1, 2, 4, 8, 16, 32)
如上,MyOtherClass拥有MyClass的成员:.x、._y、.__z以及.f。你可以通过语句super.__init__(x, y, z)初始化基类的数据成员x、._y和.__z,该语句会调用基类的.__init__方法。
除此之外,MyOtherClass还有自己的成员:.__u、.__v、.__w和.f_。
下面,我们通过vars获取数据成员:
>>> vars(d){'x': 8,'_y': 16,'_MyClass__z': 32,'_MyOtherClass__u': 1,'_MyOtherClass__v': 2,'_MyOtherClass__w': 4}
我们可以调用基类和派生类中的所有方法:
>>> d.f('some-argument')instance method f calledinstance: <__main__.MyOtherClass object at 0x7f32ef3e7048>instance attributes:{'x': 8,'_y': 16,'_MyClass__z': 32,'_MyOtherClass__u': 1,'_MyOtherClass__v': 2,'_MyOtherClass__w': 4}class: <class '__main__.MyOtherClass'>arg: some-argument>>> d.f_('some-argument')instance method f_ calledinstance: <__main__.MyOtherClass object at 0x7f32ef3e7048>instance attributes:{'x': 8,'_y': 16,'_MyClass__z': 32,'_MyOtherClass__u': 1,'_MyOtherClass__v': 2,'_MyOtherClass__w': 4}class: <class '__main__.MyOtherClass'>arg: some-argument
但是,如果派生类包含的某个成员与基类同名,则优先利用派生类的成员。
总结
面向工具编程是Python支持的编程范式之一。面向工具蕴含的抽象以及表征的现实天下行为在某些时候会非常有帮助性。然而,有时也可能会违反直觉,并为开拓过程带来不必要的麻烦。
在本文中,我们先容了如何利用Python编写基本的面向工具程序。Python中还有很多类和面向工具的功能,例如:
方法:.__repr__和.__str__
方法:.__new__
操作符
方法:.__getattribute__、.__getattr__、.__setattr__和.__delattr__
天生器
可调用性
创建序列
描述器
高下文管理
抽象类和成员
多重继续
利用super
拷贝
序列化
slot
类润色器
数据类
等等……
现如今面向工具是非常盛行的编程办法。如果你立志做一名Python开拓职员,那么就该当学习面向工具编程。但请不要忘却,Python还支持其他编程范式,例如过程式编程、函数式编程等,在某些情形下大概选用这些范例更为得当。
尽情享受编程的快乐!
原文:https://www.blog.duomly.com/object-oriented-programming-in-python/
本文为 CSDN 翻译,转载请注明来源出处。
【END】