geekdoc-python-zh/docs/pythoncentral/introduction-to-python-clas...

7.3 KiB
Raw Permalink Blame History

Python 类介绍(第 2 部分,共 2 部分)

原文:https://www.pythoncentral.io/introduction-to-python-classes-part-2-of-2/

在本系列的第一部分中,我们看了在 Python 中使用类的基础知识。现在我们来看看一些更高级的主题。

Python 类继承

Python 类支持继承,这让我们获得一个类定义并扩展它。让我们创建一个继承(或第一部分中的例子派生出)的新类:


class Foo:

def __init__(self, val):

self.val = val

def printVal(self):

print(self.val)
 derived Foo(Foo):
def negate val(self):
self . val =-self . val

这定义了一个名为DerivedFoo的类,它拥有Foo类所拥有的一切,还添加了一个名为negateVal的新方法。这就是它的作用:


>>> obj = DerivedFoo(42)

>>> obj.printVal()

42

>>> obj.negateVal()

>>> obj.printVal()

-42

当我们重新定义(或覆盖)一个已经在基类中定义的方法时,继承变得非常有用:


class DerivedFoo2(Foo):

def printVal(self):

print('My value is %s' % self.val)

我们可以按如下方式测试该类:


>>> obj2 = DerivedFoo2(42)

>>> obj2.printVal()

My value is 42

派生类重新定义了printVal方法来做一些不同的事情,每当调用printVal时都会使用这个新版本。这让我们可以改变类的行为,这通常是我们想要的(因为如果我们想要原始的行为,我们只需要使用原始的类)。注意,该方法的新版本调用旧版本,并且调用以基类的名称为前缀(否则 Python 会认为您调用的是新版本)。

Python 提供了几个函数来帮助您确定一个对象是什么类:

  • 检查一个对象是否是指定类的一个实例,或者是一个派生类。

例如以下内容:


>>> print(isinstance(obj, Foo))

True

>>> print(isinstance(obj, DerivedFoo))

True

>>> print(isinstance(obj, DerivedFoo2))

False

  • 检查一个类是否派生自另一个类

例如以下内容:


>>> print(issubclass(DerivedFoo, Foo))

True

>>> print(issubclass(int, Foo))

False

Python 类迭代器和生成器

Python 的for语句将循环遍历任何可迭代的,包括内置的数据类型,如数组和字典。例如:


>>> arr = [1,2,3]

>>> for x in arr:

...     print(x)

1

2

3

当我们定义自己的类时,我们可以使它们可迭代,这将允许它们在 for 循环中工作。我们通过定义一个返回一个迭代器(一个跟踪我们在循环中的位置的对象)的__iter__方法和一个返回下一个可用值的__next__方法来实现这一点。请注意Python 3.x 和 Python 2.x 的next方法的语法是不同的。对于 Python 3.x您必须使用__next__方法,而对于 Python 2.x您必须使用next方法。

这里有一个简单的例子,可以让你在一个数据结构上向后迭代。下面是类的定义:

[python] class Backwards: def init(self, val): self.val = val self.pos = len(val)

def iter(self): 回归自我

def next(self): #如果 self.pos < = 0: 提升 StopIteration我们就完成了

self.pos = self.pos - 1 return self.val[self.pos] [/python]

[python] class Backwards: def init(self, val): self.val = val self.pos = len(val)

def iter(self): 回归自我

def next(self): #我们完成了 如果 self.pos < = 0: 提高 StopIteration

self.pos = self.pos - 1 return self.val[self.pos] [/python]

这是一个迭代类的例子:


>>> for x in Backwards([1,2,3]):

...     print(x)

3

2

1

该类跟踪两件事,被迭代的数据结构和下一个要返回的值。__iter__方法只是返回对对象本身的引用,因为这是用来管理循环的。当 Python 遍历对象时,它会重复调用next方法来获取下一个值,直到没有剩余值时抛出StopIteration异常。

这是一个非常简单的例子,但是它的大部分是锅炉板代码(获取每一项并跟踪我们在循环中的进度),每次我们想要创建一个 iterable 类时都是一样的。然而Python 又一次拯救了我们,它给我们提供了一种方法,使用生成器来消除所有这些重复的管理代码。

一个生成器是一种特殊的函数,它返回一个 iterable 对象,这个对象自动地记住它在一个循环中的位置。这是同一个例子,这次使用了一个发生器。

该功能可定义如下(注意:使用yield关键字):


def backwards(val):

for n in range(len(val), 0, -1):

yield val[n-1]

我们可以这样使用发电机:


>>> for x in backwards([1,2,3]):

...     print(x)

3

2

1

如果你以前从未见过这种事情,你可能真的很难理解它,但是最简单的方法就是像这样阅读backwards函数:

  • 在传入的值上向后循环。
  • 在每一遍中,yield下一个值,即暂时停止执行循环,并将下一个值返回给调用者。它做它想做的任何事情,然后当它再次调用我们时,我们从我们停止的地方继续循环。

作为对象的 Python 类

一个类描述了该类的实例看起来像什么也就是说它们将有什么方法和成员变量。在内部Python 在自己的对象中跟踪每个类的定义,我们可以修改这个对象。这意味着我们可以动态地改变类的定义,甚至在运行时创建一个全新的类!

让我们从一个简单的类定义开始:


class Foo:

def __init__(self, val):

self.val = val

让我们来看看用法:


>>> obj = Foo(42)

>>> obj.printVal()

AttributeError: Foo instance has no attribute 'printVal'

哎呀!我们得到一个错误,因为这个类没有一个printVal方法。

好了,再加一个:-)。我们可以这样定义它:


def printVal(self):

print(self.val)

我们可以将函数添加到类中,如下所示:


>>> Foo.printVal = printVal

>>> obj.printVal()

42

我们定义了一个名为 printVal 的方法,它是独立的(也就是说,它是在类之外定义的),但是它看起来像一个类方法(也就是说,它有一个 self 参数)。然后,我们将它添加到类定义(Foo.printVal = printVal)中,这使得它变得可用,就好像它是原始类定义的一部分一样。

如果我们想删除它,我们可以使用普通的del语句:


>>> del Foo.printVal

>>> obj.printVal()

AttributeError: Foo instance has no attribute 'printVal'

为了在运行时创建一个全新的类,我们使用了type方法:


>>> obj = MyNewClass()

NameError: name 'MyNewClass' is not defined

>>> MyNewClass = type('MyNewClass', (object,), dict())

>>> obj = MyNewClass()

>>> print(obj)

<__main__.MyNewClass object at 0x01D79DCC>

type调用的第二个参数是我们想要从中派生的类的列表,而第三个参数是组成类定义的方法和成员变量的字典(您可以在这里定义它们,或者如上所述动态添加它们)。

要理解 Python 中的生成器和 yield 关键字,请查看文章 Python 生成器和 yield 关键字