Python 生成器和类






4.53/5 (7投票s)
在本文中,我们将进一步探讨生成器和类。
引言
这是我们关于学习 Python 及其在机器学习 (ML) 和人工智能 (AI) 中应用的系列文章的第三个模块。 在前一个模块中,我们了解了数据结构和循环。 现在让我们更深入地研究生成器和类。
生成器
创建自己的迭代器的一种方法是使用生成器函数。 生成器函数使用yield
关键字将下一个迭代器值传递给调用者。 这类似于 C# 中的 yield return
关键字。 一旦函数返回,就没有任何东西可以迭代了。
让我们通过一个生成器函数来演示 yield
关键字,该函数产生斐波那契数列的前 n 个数字
def fibonacci(n):
a = 1
b = 1
for i in range(n):
if i < 2:
yield 1
else:
c = a + b
a = b
b = c
yield c
现在你可以像使用 range
这样的函数一样使用此函数,例如在循环中
for f in fibonacci(10):
print(f)
这会打印前十个斐波那契数。
您还可以使用生成器函数来生成无限多的元素。
类
与 C# 或 Java 一样,Python 也有类。 Python 提供了面向对象编程的所有标准特性。
让我们通过一个 Python 中简单类的例子来演示
from math import sqrt
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def length(self):
return sqrt(self.x ** 2 + self.y ** 2)
__init__
方法是构造函数。
length
是一个类方法。
类方法的第一个参数是指向正在处理的类实例。 按照惯例,这被称为 self
。(你可以将其命名为其他名称,但没有人这样做。)self
的作用与 C# 和 Java 中的 this
的作用非常相似,即对当前对象的引用。 Python 的区别在于你不能只使用 x
而不是 self.x
,并且 Python 要求你显式地将其作为第一个方法参数包含在内。
现在你可以像这样使用这个类
v = Vector(1, 1)
print(v.length())
print(v.x)
print(v.y)
你可以像上面看到的那样访问 x
和 y
属性,但它们也可以被修改
v.x = 2
print(v.length())
Python 没有访问修饰符,例如 public
和 private
。所有变量都是公开可访问的。 以一个下划线开头的属性名称可以告诉你的类的用户,他们不应该使用该属性,但这并没有被该语言强制执行。
继承
让我们演示如何在 Python 中从一个类派生。我们将创建一个基类 Document 和一个派生类 Book
class Document:
def __init__(self, author, content):
self.author = author
self.content = content
def length(self):
return len(self.content)
def info_summary(self):
return "Document written by " + self.author
class Book(Document):
def __init__(self, author, content, pages):
super().__init__(author, content)
self.pages = pages
def info_summary(self):
return "Book written by {} of {} pages".format(self.author, self.pages)
Book
类从 Document
派生。 在 Book
类的 __init__
方法中,此行调用超类的构造函数。
super().__init__(author, content)
info_summary
函数在 Book
中被重写(不需要像 override
这样的关键字),并且在 Book
中没有提到 length
,因此它只是从 Document
派生的。
book = Book("me", "... content ...", 50)
print(book.length())
print(book.info_summary())
如果你想检查某个对象是否属于某个类,请使用 isinstance
函数
print(isinstance(book, Book)) # True
print(isinstance(book, Document)) # True
print(isinstance(book, object)) # True
doc = Document("someone else", "...")
print(isinstance(doc, Book)) # False
print(isinstance(doc, Document)) # True
与 C# 和 Java 不同,Python 支持多重继承:你可以写 class Book(Document, AnotherClass, PerhapsEvenMore)
,而不是写 class Book(Document)
。
如果超类的方法具有相同的名称,则只能在子类中派生其中一个方法。 当一个方法被调用(没有被显式覆盖)时,Python 使用一种名为 C3 线性化的算法来确定在超类中查找的顺序。 如果你想查看所谓的 方法解析顺序,你可以查看 YourClassName.__mro__
属性。 这是一个人工示例来演示这一点
class A:
pass
class B:
pass
class C:
pass
class D(A, C):
pass
class F(B, C):
pass
class G(A):
pass
class H(F, B, D, A):
pass
print(H.__mro__)
这会输出 (<class '__main__.H'>, <class '__main__.F'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
因此你将知道 Python 将首先查找 H 类,然后是 B、D、A,最后是 C。
魔术方法
Python 类提供了许多“魔术方法”,允许你进行运算符重载,将你的类实例视为迭代器等等。
魔术方法就像一个普通方法,但名称格式为 __method_name__
。 你已经知道一个魔术方法,__init__
。 另一个例子是 __add__
魔术方法,用于重载 +
运算符
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(3, 2)
v2 = Vector(4, 1)
v3 = v1 + v2
__iter__
和 __next__
魔术方法使你能够迭代你的实例。 此方法返回下一个迭代值,或者引发 StopIteration
以指示结束。
class Fibonacci:
def __init__(self, n):
self.prev = 1
self.prev_prev = 1
self.n = n
self.i = 0
def __iter__(self):
return self
def __next__(self):
self.i += 1
if self.i == self.n + 1:
raise StopIteration
if self.i <= 2:
return 1
else:
current = self.prev + self.prev_prev
self.prev_prev = self.prev
self.prev = current
return current
for fib in Fibonacci(10):
print(fib)
这只是魔术方法的表面,你可以做更多的事情。 如果你感兴趣,请参考这个 指南。
结论
在本模块中,我们讨论了用于迭代器的生成器函数、类、继承和魔术方法。 现在我们已经介绍了 Python 基础知识,我们可以全局了解 Python 中与机器学习相关的包。