我们课程前半部分的主要目标是向您介绍Python语言的赢博体育核心元素。在这个调查中,我们还有最后一个主要的语言元素——类。这些课堂讲稿将作为你对这个概念的第一次介绍。
在Python中,类是由成员变量和方法组成的结构。成员变量只是嵌入在类结构中的一个变量。方法是嵌入在类结构中的函数。方法通常设计为使用成员变量。
Python中的许多内置数据类型都是类。示例包括String类和List类。我们已经看到了许多与这些类一起使用的方法的例子。当您看到如下代码时,您可以判断您正在使用一个方法
> <对象变量。<方法名称>(<参数>)
下面是一些例子:
#在列表对象上使用append方法myList = [] myList.append(2) #在字符串对象上使用split方法text = " 1234 " parts = text.split() tuple = (int(parts[0]),int(parts[1]))
在这些笔记的其余部分中,您将看到如何构造自己的类和方法。
伪随机数序列是一组数字序列,在偶然的观察者看来是随机的,但它是由确定的、可重复的算法生成的。用于生成伪随机数序列的最简单算法之一是线性同余随机数生成器。这是一个生成整数序列的简单数学公式。为了形成序列,我们选择一个任意整数作为序列的种子。然后,我们将起始值替换为一个简单的线性函数,以生成序列中的下一个数字。最常见的是,简单的线性函数使用两个大素数作为它的系数。
X1 = 12553 x0 + 15401
我们重复这个过程来得到一个完整的数列。如果我们这样做,这个数列通常会趋向正无穷或负无穷。为了防止这种情况,我们在每一轮中对每个数字取一个大质数b的模。
X1 = (12553 x0 + 15401) mod 6133
这保证了数字被困在从0到b-1的范围内。通常,由该过程生成的整数序列不会立即对我们有用,因为,比如说,我们可能对生成从0到c-1范围内的其他整数c感兴趣。这很容易修复。如果c < b,我们可以为生成的每个数字计算x % c,并在生成时保存x % c值的列表。
下面是一个简单的Python示例程序,它使用此过程生成并打印一个0-99范围内的整数列表:
#在(100)范围内n的伪随机序列的种子值:print(x % 100) x = (12553*x+15401)%6133
接下来,我们希望以某种方式封装随机数生成过程,以便我们可以根据需要启动和停止该过程。这样做的关键是将x的当前值放在一个安全的位置,以便我们可以在需要时访问它以生成序列中的下一个x。这是一个很好的Python类概念赢博体育程序。Python类以成员变量的形式存储状态信息。在本例中,我们将创建一个随机序列类,它将x变量的当前值存储为成员变量。接下来,我们还需要为类配备至少一个方法,客户机可以使用该方法从序列中获取新数字。
下面是一个Python类的代码,它可以完成这些事情。
class RndSeq(): def __init__(self,seed = 0): self。x = seed def nextInt(self,N): ““”返回0到N-1范围内的随机整数。”“”如果N > 5000: print("RndSeq。nextInt要求它的参数小于或等于5000 ")返回0 self。X = (12553*self. X +15401)%6133返回self。x % N
这段代码显示了创建一个有用的类所需要的最少的东西。该类包含一个__init__方法,用于初始化对象。在本例中,需要用种子值初始化成员变量x。由于类在成员变量中存储有用的信息,因此还需要成员函数来访问和修改这些值。在本例中,我们只需要一个成员函数nextInt(),它将计算并存储序列中的下一个x,并根据对象中隐藏的x的新值返回结果。
这段代码存储在一个名为“rndseq.py”的文件中。我们可以在一个简单的测试程序中导入和使用这个类。
from rndseq import rndseq seed = input(“为序列输入一个种子值:”)s = rndseq (int(seed)) for n in range(100): print(s. nextint (100))
以下是关于RndSeq类和示例程序中的代码需要注意的一些事项:
自我。<变量>
语法。自我
作为第一个参数。自我
引用该方法正在处理的对象。<对象> .method ()
语法。例如,在上面的代码中,我们调用nextInt ()
方法在RndSeq对象上年代
。Python会自动翻译s.nextInt (100)
来nextInt(s,100)
。在nextInt ()
方法年代
被分配给参数自我
100被赋值给参数N
.<类名>(<参数)
。例如,在上面的示例代码中,我通过调用创建一个RndSeq对象RndSeq (int(种子))
。当您以这种方式创建对象时,Python将把您提供的任何参数传递给类的__init__ ()
方法。的__init__ ()
方法将初始化新创建对象中的成员变量。对象在其成员变量中存储状态信息。方法操作这些成员变量。
在下一个例子中,我们将构造一个类来玩一个简单的猜谜游戏。当你创建一个游戏对象时,我们类的__init__()方法将从1到100的范围内随机选择一个整数开始。游戏对象也会记录你猜测的次数。要玩这个游戏,您需要将猜测传递给guess()方法。这种方法会将你的猜测与秘密数字进行比较,并告诉你是太低、太高还是刚刚好。每次调用guess()方法时,游戏都会根据您的猜测次数增加其计数。如果你猜了太多而没有得到正确答案,游戏对象将不允许你再猜。
导入随机类GuessingGame(): def __init__(self): self。Secret = random.randint(1,100) self。defisgameover(自我):如果猜测< 5:返回False否则:返回True def guess(自我,n):如果自我。# # # # # # # # # # # # # # # # # # # # # # # # # # # # #秘密:自己。猜想+= 1 print(“你的猜测太低了”)elif n > self。秘密:自己。guess += 1 print("Your guess is too high.") else: self。猜对了!你赢了。”)
这里有一些代码来玩这个猜谜游戏。
game = GuessingGame() while game. isgameover () == False: n = int(input("Guess a number:")) game. Guess (n))
对于我们的下一个Python类示例,我将构造一个表示复数的类。这是一个非常简单的类,只有两个数据成员,即数字的实部和虚部。该类包含读取这些数据成员以及计算和返回复数的模数的方法。
因为我们希望能够对复数对象进行算术运算,所以我还为这个类提供了许多操作符重载来实现各种操作。
当您尝试对一对数据项执行操作时,例如添加两个Complex对象,Python将尝试通过将代码转换为类似于方法调用的内容来理解该操作。这里有一个例子。假设我们编写代码
a = Complex(2,3) b = Complex(3,4) c = a*b
当Python解释器到达最后一条语句时,它会尝试通过首先将代码转换为另一种形式来理解那里的代码:
C = a.__mul__(b)
这具有将方法调用赢博体育于具有参数b的对象a的形式。为了使其工作,Python将检查存储在a中的对象,以查看它是否具有同名的方法。如果有,它会调用那个方法。
下面是Complex类中__mul__方法的代码。
def __mul__(self,other): r = self.real*other. getreal () - self. image *other. getimag () i = self.real*other. getimag () + self. image *other. getreal () return Complex(r,i)
此方法使用该操作的标准数学规则实现复杂乘法。在计算出产品的实部和虚部之后,该函数构造并返回一个包含这些部分的Complex对象。
在类中提供的另一个方便的方法是__str__方法。当您尝试使用str()将对象转换为文本时,将调用此方法。这里有一个例子。
x = Complex(2,1) print(str(x))
当Python解释器遇到第二条语句时,它将尝试将其重写为
print (x.__str__ ())
如果x是一个对象,并且x所属的类实现了__str__方法,则此操作将成功。
下面是Complex类的__str__方法的代码。
Def __str__(self):如果self。Imag < 0:返回str(self.real)+"-"+str(-self. Imag)+"i" elif self。Imag > 0:返回str(self.real)+"+"+str(self.imag)+"i" else:返回str(self.real)
下面是Complex类的完整源代码。这段代码位于一个名为complex.py的独立源文件中。
import math class Complex(): def __init__(self,real,imag = 0): self。真实的自我。imag = imag def getAbs(self):返回math.sqrt(self.real*self.real+self.imag*self.imag) def __add__(self,other):返回Complex(self.real+other.real,self.imag+other.imag) def __sub__(self,other):返回Complex(self.real-other.real,self. image -other.imag) def __mul__(self,other): r = self.real*other。真实-自我。想象*他人。image I = self.real*other。image + self. image *other。real return Complex(r,i) def __truediv__(self,other): d = other.real*other。Real + other. image *other。imagr = (self.real*other.)Real + self. image *other. image) / d I = (self. image *other. image)real - self.real*other. imagag) / d返回Complex(r,i) def __str__(self): if self.real .realImag < 0:返回str(self.real)+"-"+str(-self. Imag)+"i" elif self。Imag > 0:返回str(self.real)+"+"+str(self.imag)+"i" else:返回str(self.real)
下面是一个利用复数类的赢博体育程序。这个程序是牛顿方法的一个实现,它利用了牛顿方法在复数存在时工作得很好的事实。
这个程序试图找到多项式的根
X4 + 2x3 - 6x2 + 8x + 80
这个多项式没有实根:它的四个根都在复平面上。如果你用一个复数作为开始的猜想来运行这个程序它将收敛于这四个复根之一。如果你用一个实数作为开始的猜测来启动程序,牛顿的方法就会陷入一个无限循环并且不会收敛。当发生这种情况时,您必须通过在IDE中选择stop命令或在终端中按control-C组合键来停止程序。
用牛顿法求出f(x) = x^4 + 2 x^3 - 6 x^2 + 8 x + 80的根。这个多项式有四个复根。定义函数及其导数def (x): return ((x+ complex (2))*x+ complex (-6))*x+ complex (8))*x + complex (80) def fp(x): return ((complex (4)*x+ complex (6))*x+ complex (-12))*x+ complex (8) r = input(“输入您的开始猜测的实部:”)i = input(“输入您的开始猜测的虚部:”)x = complex (float(r),float(i)) y = f(x)公差= 10e-6而y.getAbs() >公差:#计算f'(x) yp = fp(x) #执行一轮牛顿方法x = x - y/yp y = f(x) print(“根估计是”+ str(x))
这里需要注意的重要一点是赢博体育出现在f(x)及其导数定义中的数字都必须是复数。我们需要这样做,因为允许我们对复数对象进行算术运算的运算符函数要求对复数对象进行运算的两个操作数。
构造一个类来表示有理数。有理数包含一个分子和一个分母,它们都是整数。要使有理数类有用,请为其提供加法、减法、乘法和除法的运算符重载。还提供__str__()方法将有理数转换为字符串。最后,在类中添加__float__()方法,以便您可以根据需要将有理数转换为浮点数。
为了检查你的有理数算术函数是否正确工作,写一个程序来计算π的两个有理数近似值。估算π值的一种方法是使用莱布尼茨发现的这个公式:
另一种方法是由于沃利斯:
当你增加每个近似值中的项数时,你最终会生成一个接近π的有理数序列。编写一个程序,使用这两种方法来构造π的两个有理逼近序列。每次在近似值中添加另一项时,使用float()函数将有理数转换为浮点数并打印该数字。