在机器学习和人工智能领域,Python语言因其简洁和强大的库支持而备受欢迎。在之前的模块中,探讨了数据结构和循环。现在,让进一步了解生成器和类的概念。
生成器是创建自定义迭代器的一种方式。生成器函数使用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)
在Python中,__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", "... content ...")
print(isinstance(doc, Book))
# False
print(isinstance(doc, Document))
# True
与C#和Java不同,Python支持多重继承:可以写成class Book(Document),也可以写成class Book(Document, AnotherClass, PerhapsEvenMore)。如果超类中有同名的方法,子类只能派生其中的一个。当调用一个方法(没有明确覆盖)时,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__)
这将输出(
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)
__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)
这只是魔术方法的表面,可以做更多。如果感兴趣,可以参考这个指南。