Program Arcade Games
With Python And PygameChapter 12: 类的入门
类和对象是非常强大的编程工具。它们让编程变得更简单。事实上,你应该已经很熟悉类和兑现的概念了。 一个类是一个对象的“类别”。就像 “人” 或者 “图像image”。一个对象是一个类的具体实例。就像“玛丽”是“人”的一个实例。一个p>
对象有各种属性,比如人的名字,身高,年龄。 对象也有方法。方法定义了对象可以做什么,比如跑,跳,坐。
12.1 为什么要学习类?
每个冒险游戏中的角色需要数据: 名字,位置,力量,他们是否举着手臂,它们要去的方向,等等。 再加上角色还有动作do。他们要跑,跳,打,说。
没有类,我们的Python代码需要想这样存储数据:
name = "Link" sex = "Male" max_hit_points = 50 current_hit_points = 50
为了让这个角色可以有行为,我们需要把这些数据传递给一个函数:
def display_character(name, sex, max_hit_points, current_hit_points): print(name, sex, max_hit_points, current_hit_points)
现在想象创建一个程序,它有一组变量包含了包含游戏中需要的每个角色,怪兽,和物品。然后哦我们需要创造和它们相关的函数。 我们现在已经涉及到了一大堆数据。突然之间这听起来一点都不好玩。
但是等下,还在变得更糟! 随着游戏的扩展,我们的角色可能需要更读的属性。 下面例子中,我么添加了我们bd>max_speed:
name = "Link" sex = "Male" max_hit_points = 50 current_hit_points = 50 max_speed = 10 def display_character(name, sex, max_hit_points, current_hit_points, max_speed): print(name, sex, max_hit_points, current_hit_points)
在上面的例子中,只有一个函数。但是在大型视频游戏中,我们可能对角色就有上百的函数。每次给角色添加一个新的属性,就需要我们遍历每个函数,给它们都添加这么一个参数。 这会产生 巨大的工作量。 而且我们可能需要把max_speed添加到各种不同哦角色例如各种野兽。 所以我们需要一种更好的办法。我们的程序需要把这些数据属性都打包起来,便于管理。
12.2 定义和创建简单的类简单
一种更好的管理多个数据属性的办法是 定义一种结果能够包含所有信息。 然后我们能给这些信息的集合一个名字,比如 Character或者Address。通过使用 类,Python和其他现代语言都可以轻松完成。
例如,我们可以定义一个类来表示游戏中的一个角色:
class Character(): """ This is a class that represents the main character in a game. """ def __init__(self): """ This is a method that sets up the variables in the object. """ self.name = "Link" self.sex = "Male" self.max_hit_points = 50 self.current_hit_points = 50 self.max_speed = 10 self.armor_amount = 8
这是另一个例子,我们定义了一个类来包含一个地址所需的数据。:
class Address(): """ Hold all the fields for a mailing address. """ def __init__(self): """ Set up the address fields. """ self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = ""
在上面的代码中, Address是类的名字。砸类中的变量,比如name和city, 被称作attributes(属性)或者fields(域)。 (请注意在声明类和函数时的相同和不同)
和函数与变量不同,类的名字应该以大写字母开头。尽管小写字母开头也是可以的,但是不推荐。
def __init__(self):是一个特殊函数,叫做 constructor(构造函数),会在类创建的时候自动运行。我们会待会儿单独讨论下构造函数。
self就像是是my(我的)意思。当在类Address之内是,我么讨论的是我的 name,我的city,等等。 我们不想在Address类的定义之外使用self.来指代一个Address的属性。为什么?因为就像“我的”,它意味着其他说的时候指的完全是另一个人啊!
为了更好的将不同的类的关系可视化,程序员经常会画图表。一张Address类的图标如图例所示12.1。 我们可以看到类的名字和各属性的列表在最上方。每个属性的右边是它的数据类型,比如字符串或者整数。
类的代码定义了一个类但是并没又真正地创造它的一个实例。代码告诉计算机一个地址有什么属性和它们的初始值会是多少。 我们并没有真的拥有一个地址。 我们可以定义一个类而不创建,就像我们可以定义一个函数而不调用。 要创建一个类并设置属性,请看下面的例子:
# 创建一个地址 home_address = Address() # 设置地址的属性 home_address.name = "John Smith" home_address.line1 = "701 N. C Street" home_address.line2 = "Carver Science Building" home_address.city = "Indianola" home_address.state = "IA" home_address.zip = "50125"
第二行创建了一个地址类的实例。请注意类的名字Address是如何使用的,以及紧跟的圆括号。 变量名只要符合命名规则都可以。
要设置一个类里的属性,程序必须使用点运算符。 这个运算符就是home_address和属性名称之间的句点。 看一下5到10行是如何用点运算符设置每个属性的值的。
在使用类时一个非常常见的错误是忘记是哟的是使用哪一个实例。 如果只创建了一个地址,很自然地会假设计算机知道你所使用的地址 但这不并不是事实。请看虾米的例子:
class Address(): def __init__(self): self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = "" # 创建一个地址 my_address = Address() # 警告! 这并不能设置地址的名字! name = "Dr. Craven" # 这也不能设置地址的名字 Address.name = "Dr. Craven" # 这是可行的 my_address.name = "Dr. Craven"
我们可以创建第二个地址,并同时使用两个实例的属性,如下例所示:
lass Address(): def __init__(self): self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = "" # 创建一个地址 home_address = Address() # 设置地址的属性 home_address.name = "John Smith" home_address.line1 = "701 N. C Street" home_address.line2 = "Carver Science Building" home_address.city = "Indianola" home_address.state = "IA" home_address.zip = "50125" # 创建另一个地址 vacation_home_address = Address() # 设置地址的属性 vacation_home_address.name = "John Smith" vacation_home_address.line1 = "1122 Main Street" vacation_home_address.line2 = "" vacation_home_address.city = "Panama City Beach" vacation_home_address.state = "FL" vacation_home_address.zip = "32407" print("The client's main home is in " + home_address.city) print("His vacation home is in " + vacation_home_address.city)
11行创建了Address类的第一个实例; 22行创建了第二个实例。 变量名home_address指向第一个实例,而vacation_home_address指向第二个。
25到30行设置了新的实例的各个属性。32行打印了家庭地址的城市,因为home_address出现在点运算符之前。 33行打印了度假地址因为vacation_home_address出现在点运算符之前。
在例子中,Address被称为类是因为它定义了对一个数据对象的新的类别。而变量home_address和vacation_home_address分别指向对象,因为它们指向了 Address类中的具体实例。对象的一个简单定义就是,它是一个类的一个具体实例。例如“Bob”和“Nancy”是Human类的实例。
通过使用www.pythontutor.com我们可以将代码的执行进行视觉化。 这里有三个变量。第一个指向Address类的定义。另外两个指向不同地址的对象和它们的数据。
把许多数据的属性放入一个类,让对函数数据的传入和传出便捷不少。在下面的代码中,函数以参数取了一个地址,并将其打印到了屏幕上。 这样就没有必要把每个地址的属性作为参数传递了。
# 打印一个地址到屏幕上 def print_address(address): print(address.name) # 如果地址里有一个line1,打印它 if len(address.line1) > 0: print(address.line1) # 如果地址里有一个line2,打印它 if len(address.line2) > 0: print( address.line2 ) print(address.city + ", " + address.state + " " + address.zip) print_address(home_address) print() print_address(vacation_home_address)
12.3 给类添加方法
除了属性,类还可以拥有方法。 方法是存在于类中的函数。下面的代码展开了先前Dog类的例子,添加了一个犬吠的方法。
class Dog(): def __init__(self): self.age = 0 self.name = "" self.weight = 0 def bark(self): print("Woof")
方法的定义在6-7行。 类中的方法的定义看起来和函数的定义一模一样。 重要的区别是在第6行添加了一个参数 self。类中的方法的第一个参数必须是self。 这个参数是必须的,尽管函数并不使用它。
西面是在给类创建方法是要牢记的事项:
- 属性应该最先列出来,方法其次。
- 任何方法的第一个参数必须是self。
- 方法的定义的缩进是正好一个制表符(tab)。
方法的调用,和引用一个对象的属性相类似。请看下面的代码示例:
my_dog = Dog() my_dog.name = "Spot" my_dog.weight = 20 my_dog.age = 3 my_dog.bark()
第一行创建了一条狗。3-5行设置了对象的属性。第7行调用了bark方法。请注意尽管 bark方法有一个参数, self, 调用并没有传递任何东西。这是因为第一个参数是用来指向对象本色的。 在背后,Python做了像这样的一个调用:
# 例子, 实际上并不合法 Dog.bark(my_dog)
如果bark方法需要指向任一属性,它需要使用self作为引用的变量。 例如,我们可以改变Dog的类,让它在狗叫的时候,打印出狗的名字。 在下面的代码中,可以通过点运算符和 self的指向来访问名字这一属性。
def bark(self): print("Woof says", self.name)
属性是形容词,方法是动词。把类画成图大概如图例所示12.3.
12.3.1 避免这个错误
把所有要所有方法内所有东西都放进一个定义。不雅定义两次。 For example:
# 错误的: class Dog(): def __init__(self): self.age = 0 self.name = "" self.weight = 0 def __init__(self): print("New dog!")
计算机会自动无视第一个__init__,而取最后的定义。我们其实应该这样做:
# 正确的: class Dog(): def __init__(self): self.age = 0 self.name = "" self.weight = 0 print("New dog!")
12.3.2 例子:球的类
这段代码可用于Python/Pygame来画一个球。 把所有的参数放入一个类让数据管理变得更简单了。 Ball类的图标如图例所示12.4。
class Ball(): def __init__(self): # --- 类的属性 --- # Ball position self.x = 0 self.y = 0 # 球的矢量 self.change_x = 0 self.change_y = 0 # 球的尺寸其秋 self.size = 10 # 球的颜色 self.color = [255,255,255] # --- 类的方法 --- def move(self): self.x += self.change_x self.y += self.change_y def draw(self, screen): pygame.draw.circle(screen, self.color, [self.x, self.y], self.size )
下面的代码需要放在主循环之前来创建一个球并设置其属性:
theBall = Ball() theBall.x = 100 theBall.y = 100 theBall.change_x = 2 theBall.change_y = 1 theBall.color = [255,0,0]
这段代码要放在主循环内来移动和绘制球:
theBall.move() theBall.draw(screen)
12.4 引用
这里将是我们区别真正的程序员和想要成为程序员的地方。 理解类的引用。 下看一下下段代码:
class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 nancy = Person() nancy.name = "Nancy" print(bob.name, "has", bob.money, "dollars.") print(nancy.name, "has", nancy.money, "dollars.")
上述代码创建了Person()类的两个实例,使用 www.pythontutor.com我们可以视觉化这两个实例,如图例所示12.5.
上面的代码并无新意。但是下面的代码不一样了:
class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 nancy = bob nancy.name = "Nancy" print(bob.name, "has", bob.money, "dollars.") print(nancy.name, "has", nancy.money, "dollars.")
看到第9行的区别了吗区别
使用对象时一个常见的概念错误是,假设变量bob 是Person的一个对象。这里并不是。变量bob是Person对象的一个象em>引用(reference)。也就是说,它存储了对象所在位置的内存地址,而不是对象本身。
如果bob真的就是对象,那么第9行可以创建一个对象的复制品,这样就会存在两个对象。 程序的输出会变成Bob和Nancy都有100美元。但真正运行的时候,程序的输出其实是这样的:
Nancy has 100 dollars. Nancy has 100 dollars.
bob所存储的是对对象的一个引用。除了引用, 还可以称它为地址, 指针, 或者柄. 一个引用是一个内存地址,存储了对象所在的地址。这个地址是个16进制数,如果打印出来,可能是0x1e504之类的。 当第9行运行的时候,地址被复制了而不是地址指向的整个对象。参考图例12.6。
我们可以在www.pythontutor.com内运行来看看两个变量是如何指向同一个对象的。
12.4.1 函数和引用
来看下面这段代码。第1行创建了一个取数字作为参数的函数。 变量money是一个变量,它变量了传入数据的赋值皮。添加100并不会改变存储在第10行中bob.money中的数字。因此,13行打印出来的是100,而不是200。
def giveMoney1(money): money += 100 class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 giveMoney1(bob.money) print(bob.money)
运行在PythonTutor 中我们可以看到哟两个money变量的实例。一个是复制品,并对于giveMoney1函数来说是局部的变量。
再看下一段额娃的代码。这段代码虎造成bob.money增加,并打印出200。
def giveMoney2(person): person.money += 100 giveMoney2(bob) print(bob.money)
这是为什么?因为person中包含了对象地址的复制品,而不是真正的对象。我们可以把它想成一个银行帐号的数字。 函数有一个银行帐号数字的复制品,而不是整个银行帐号的复制品。所以使用帐号数字的复制品来存储100美元会造成银行帐号的余额上涨。
数组的工作原理也是一样的。一个函数可以取一个数组(列表)作为一个参数;改变那个数组中的值,会更改调用它的同一个数组。只是数字的地址被复制了,而不是整个数组。
12.4.2 复习题
- 创建一个叫Cat的类。给它如下属性:名字,颜色,重量。给它一个方法叫做meow。
- 创建一个它的实例,设置其属性,并调用meow这个方法
- 创建一个叫Monster的类。给它一个名字的属性,和一个整数作为生命值这一属性。创建一个方法叫decreaseHealth,取一个amount作为参数并从生命值中减去它。 在方法之内,打印出动物的名字如果它的生命值小于0。
12.5 构造函数
下面列出来的Dog类有一个严重的问题。当我们创造一条狗的时候,默认情况下它是没有名字的。狗应该是有名字的! 我们不能允许狗出生时没有名字。 然后下面的代码就让这发生了,狗永远没有一个名字。
class Dog() def __init__(self): self.name = "" my_dog = Dog()
Python不想让这发生。 这也是为什么Python的类有一个特殊的函数,在每一个类的实例创建出拉的时候会自动调用。通过添加一个构造函数,一个程序员可以添加一些代码让每次实例创建时自动运行。 如下面这个构造函数的例子:
class Dog(): def __init__(self): self.name = "" # 构造函数 # 当创建这个类西的实例时会调用类型的类型def __init__(self): print("A new dog is born!") # 这会创造一条狗 my_dog = Dog()
构造函数开始于第6行。它必须以__init__命名。 在init之前有两个下划线,之后也有两个下划线。 一种常见的错误是只用一个。常见
构造函数必须以self作为第一个参数,就像类中的其他方法咿呀。
当程序运行的时候,它会在第十行创建狗的一个对象时打印:
A new dog is born!
,这个__init__函数是自动调用的,信息也会打印到屏幕锁。
构造函数可用于对象数据的初始虎。上面Dog类的例子中允许 在创建狗的对象是name属性留成空白。我们如何阻止这样的情形发生? 许多对象在它们创建的时候就应该有值。构造函数就是这个目的。请看下面的代码:
class Dog(): # 构造函数 # 当创建这个类型的实例时会调用 def __init__(self, new_name): self.name = new_name # 这会创造一条狗 my_dog = Dog("Spot") # 打印名字,检查是不是已经设定好谁都能够nt(my_dog.name) # 这一行会报错 # 因为名字没有被传入 herDog = Dog()
第6行中的构造函数想在有了一个额外的参数叫做new_name。 这个函数的值会被用来在第7行设置Dog中的name属性。 现在不会再有无名值狗诞生了。第16行尝试了一下。Python会报出一个错误并不执行。 一个常见的错误是,把__init__函数中的参数的命名和属性名一致,以为这样它们的值寄会自动同步。这是不会发生的。
12.5.1 复习题
- 类的名称应该是大写字母还算小写字母开头?
- 方法的名称应该是大写还是小写字母打头?
- 属性的名称应该是大写哈市小写字母开头?
- 在类中哪一项应该先列出来,属性还是方法?
- 引用的其他名称叫什么?
- 实例变量的另一个名称是什么是
- 类的一个实例的另一个名字叫什么?
- 创建一个叫Star的类,它会在对下每次创建时打印出“A star is born!”
- 创建一个叫Monster的类,属性包括生命值和名字。 添加一个构造函数来通过传入的参数来设定对象的生命值和名字。
12.6 继承
使用类和对象的另一个强大功能就是有能力使用inheritance(继承)。可以通过创建一个类并继承其parent class(父类)的所有属性和方法。
例如,一个程序可能创建了一个类叫Boat,它包含了游戏中表示一艘船的所需属性:
class Boat(): def __init__(self): self.tonnage = 0 self.name = "" self.isDocked = True def dock(self): if self.isDocked: print("You are already docked.") else: self.isDocked = True print("Docking") def undock(self): if not self.isDocked: print("You aren't docked.") else: self.isDocked = False print("Undocking")
来测试我们的代码:
我们的程序也许要一艘潜水艇。我们的潜水艇可以做船能做的一切事情,再加上我们需要一个命令叫做submerge。不用继承的话我们有两个选项。
- 一是,在我们船里添加submerge()这个命令。这不是一个好主意因为我们不想给人的印象是普通的船都会下潜。
- 第二种选择,我们可以创建一个Boat类的复制品,并把它叫做Submarine。在这个新类里我们添加submerge()名利。这第一眼看上去很简单, 但是如果我们该比了Boat类时期就就变得复杂了。程序员必须得记住我们既要更改Boat类,还要对Submarine类作出相同的改变。 让这些代码同步是很费时间而且容易出错的。
幸运的是,有一种更好的办法。我么的程序可以创建一个child classes(子类),它会继承所有parent class(父类)的属性和方法。子类之后可以更具自己需要添加新的属性和方法。例如:
class Submarine(Boat): def submerge(self): print("Submerge!")
第一行非常重要。质押在类生命的时候把Boat放在圆括号之内,我们就自动获取了Boat类中所有的属性和方法。如果我们更新Boat,那么子类Submarine也会自动更新。继承就是这么简单!
下面的代码已经图表化,如图例所示12.10.
class Person(): def __init__(self): self.name = "" class Employee(Person): def __init__(self): # 先调用父类的构造函数 super().__init__() # 闲杂设置我们自己的变量 self.job_title = "" class Customer(Person): def __init__(self): super().__init__() self.email = "" john_smith = Person() john_smith.name = "John Smith" jane_employee = Employee() jane_employee.name = "Jane Employee" jane_employee.job_title = "Web Developer" bob_customer = Customer() bob_customer.name = "Bob Customer" bob_customer.email = "send_me@spam.com"
把Person放置在第5行和第13行的括号内,程序员就告诉了计算机Person对于Employee和Customer来说是一个父类。 这允许程序能在第17行和第21行设置name这一属性。
方法也可以被继承。任何父类拥有的方法,子类都可以获得。但是如果我们有一个同样名字的方法在子类和父类都存在呢?
我们哟两个选项。我们可以都用super()这个关键词来运行它们。 使用super()紧跟一个点运算符,最终可以让你调用父类版本的方法。
上面的代码展示了第一种选项,使用super来表示我们不经运行子类的构造函数,也运行父类的构造函数。
如果你在给子类写一个方法并想调用一个父类的方法,通常这会是子类方法的第一个语句。注意上述例子是怎么写的。
所有的构造函数需要调用父类的构造函数,因为如果你是一个没有父母的孩子那真是太悲惨了。事实上,有些语言是强行执行这条规则的,但是Python没有。
第二种选项? 方法可以被子类overridden(重写)来提供不同的功能。下面的例子就分别展示了两种选项。 Employee.report重写了Person.report因为它从来不掉用或者运行父类的report方法。Customer的report的确会调用父类,所以它的方法report方法会添加Person没有的新功能。
class Person(): def __init__(self): self.name = "" def report(self): # 基本报告 print("Report for", self.name) class Employee(Person): def __init__(self): # 先调用父类构造函数 super().__init__() # 现在设置我们的变量 self.job_title = "" def report(self): # 这里我们重写了report方法,并这样子做这样子 print("Employee report for", self.name) class Customer(Person): def __init__(self): super().__init__() self.email = "" def report(self): # 运行父类的report方法: super().report() # 现在添加我们自己的东西在末尾 print("Customer e-mail:", self.email) john_smith = Person() john_smith.name = "John Smith" jane_employee = Employee() jane_employee.name = "Jane Employee" jane_employee.job_title = "Web Developer" bob_customer = Customer() bob_customer.name = "Bob Customer" bob_customer.email = "send_me@spam.com" john_smith.report() jane_employee.report() bob_customer.report()
12.6.1 Is-A关系和Has-A关系
类与类之间有两种蛀牙的关系。分别是 “is a”和“has a”关系。
一个父类总是一个比子类更广义更抽象版本的类。 这种父子关系被称作is a关系。 例如,一个父类叫动物可以用一个子类叫狗。 狗类可以有一个子类叫贵宾犬。 另一个例子,海豚is a(是一种)动物。反过来并不成立,一种哺乳动物并不一定是一种海豚。所以海豚类不可能是哺乳动物类的父类。 类似的,一个叫桌子的类不会是一个叫椅子的类的父类,因为一张椅子不是一张桌子。
另一种关系叫has a(有一个)一个em>关系。
这类关系由类的属性来实现。一只狗有一个名字,所以狗类有一个属性叫名字。
相类似地,一个人可以有一条狗,这样可以通过一个叫热的类,其中有一个属性叫狗来实现。
反过来的时候,人的类不能有狗的类来推导,因为这会是一种侮辱。
通过先前的例子我们可以看到:
静态变量和实例变量的区别是令人昆虎的。幸运的是,我们现在不需要完全理解其不同。
但是你坚持写程序,你会遇到的。
所以我们在这只是简单介绍一下。 Python还有一些奇怪的地方让我一直很困惑,尤其是在我发布这本书的前几年。所以你可能会看到一些老的视频里的例子是我搞错的地方。 一个instance variable(实例变量)是我们到目前都砸使用的在种类的变量。每个类的实例得到它自己的值。例如,一个满是人的房间里,每个人有自己的年龄。
有些人的年龄也许是相同的,但是我们依然需要单独来记录它们。 在使用实例变量时,我们不能对着一屋子人说“年龄”。
我们必须要具体声明我们在讨论谁的年龄。同时,如果房间里一个人也没有,那么谈论一个人的年龄也是没有意义的。 而对于static variables(静态变量),对类的每个实例赖斯它的值都是相同的。入股一个实例都不存在,还是会有一个静态便俩的值。
例如,我们会有一个count的静态变量来记录Human类里人的存在。没有人? 值是0,但还是存砸的。 在下面的例子中,ClassA创建了一个实例变量。
ClassB创建了一个静态变量。 在上面的例子中,第16行和第17行打印出了静态变量。第17行是适当的方法去这么做。不像之前,我们现在能够在使用静态变量时引用类的名字,而不是一个指向个别实例的变量名字。
因为我们在使用类的名字,我们通过观察第17行就立刻能知道我们在使用静态变量。第16行可能是一个实例变量也可能是一个静态变量,所以为避免混淆第17行的方法更好。 第22行打印出了实例变量,就跟先前的例子一样。
第23行会产生一个错误,是因为每个y的实例都是不一样的,我们并没有告知计算机哪个ClassA的实例是我们所讨论的。 这是我不喜欢的Python功能。我们可以同时拥有相同名字的静态变量和实例变量。请看虾米的例子: 允许实例变量隐藏静态变量这一功能已经让我困惑了好多年! Click here for a multiple-choice quiz.
Click here for the chapter worksheet.
Click here for the chapter lab.
You are not logged in. Log in here and track your progress.
12.7 静态变量 vs. 实例变量
# 实例变量的例子
class ClassA():
def __init__(self):
self.y = 3
# 静态变量的例子
class ClassB():
x = 7
# 创建类的实例类a = ClassA()
b = ClassB()
# 两种打印静态变量的方法。印# 第二种方法是更适当的。
print(b.x)
print(ClassB.x)
# 一种打印一个实例变量的方法。
# 第二种方法会产生一个错误,因为我们不知道引用的是哪个实例
print(a.y)
print(ClassA.y)
12.7.1 实例变量隐藏起静态变量
# 有静态变量的类
class ClassB():
x = 7
# 创建一个它的实例的b = ClassB()
# 这会打印出7
print(b.x)
# 这也会打印出7
print(ClassB.x)
# 用类的名字来把x设为一个新的值
ClassB.x = 8
# 这也会打印出8
print(b.x)
# 这虎打印会
print(ClassB.x)
# 用实例把x设为一个新的值。
# 等一下! 事实上,这不会把x设成一个新的值!
# 它会创建一个新的变量,x。
# 这个x是一个实例变量。 The static variable is
# 静态变量也叫x。但是它么是不同的变量。
# 这太搞脑筋了,这样写很不好。写# practice.
b.x = 9
# 这会打印出9
print(b.x)
# 这回打印出8。不是9!!!
print(ClassB.x)
12.8 复习
12.8.1 选择题小测验
12.8.2 练习表
12.8.3 实验
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin