Python 入门教程/类
在编程中,你将会了解到程序员喜欢偷懒。如果有些东西已经做过,为什么要再做一遍呢?
这就是 Python 中函数所涵盖的。你的代码已经做过一些特别的事情了,现在你想再做一遍。你把那段特殊的代码放进一个函数,并尽可能地重复使用它。你可以在代码中的任何地方引用一个函数,计算机始终会知道你在说什么。方便吧?
当然,函数也有其局限性。函数不像变量那样存储任何信息 - 每次运行函数时,它都会从头开始。但是,某些函数和变量彼此关系非常密切,并且需要经常相互作用。例如,想象一下你有一根高尔夫球杆。它包含有关它的信息(即变量),例如杆身的长度、握柄的材质和球头的材质。它还与之相关的函数,例如挥动高尔夫球杆的函数或因纯粹的沮丧而将其打碎的函数。对于这些函数,你需要知道杆身长度、球头材质等的变量。
使用普通的函数可以轻松地解决这个问题。参数会影响函数的效果。但是,如果一个函数需要影响变量呢?如果每次使用你的高尔夫球杆时,杆身都会变得更弱,握柄上的手柄会磨损一点,你会变得更加沮丧,并且在球头上会形成新的划痕?一个函数无法做到这一点。一个函数只能生成一个输出,而不是四个或五个,或五百个。我们需要一种方法将密切相关的函数和变量分组到一个地方,以便它们可以相互作用。
你可能不止一根高尔夫球杆。如果没有类,你需要为每根不同的高尔夫球杆编写一大堆代码。这很痛苦,因为所有球杆都共享共同的特征,只是有些属性有所改变 - 例如杆身由什么制成以及它的重量。理想的情况是,有一个基本高尔夫球杆的设计。每次创建一个新球杆时,只需指定其属性 - 杆身的长度、重量等。
或者,如果你想要一根具有额外功能的高尔夫球杆呢?也许你决定在你的高尔夫球杆上安装一个时钟(为什么,我不知道 - 这是你的主意)。这是否意味着我们必须从头开始创建这根高尔夫球杆?我们必须首先为基本高尔夫球杆编写代码,然后再编写所有这些代码,以及时钟的代码,用于我们的新设计。如果我们只是拿我们现有的高尔夫球杆,然后将时钟的代码附加到它上面,会不会更好?
这些问题是面向对象编程解决的。它以一种能够相互看到并一起工作、根据需要复制和修改的方式将函数和变量组合在一起,而不是在不需要的时候这样做。我们使用名为“类”的东西来实现这一点。
什么是类?将类想象成一个蓝图。它本身并不是什么东西,它只是描述了如何制造东西。你可以从这个蓝图创建很多对象 - 在技术上称为实例。
那么如何创建这些所谓的“类”呢?非常简单,使用class运算符
- 代码示例 1 - 定义一个类
# Defining a class class class_name: [statement 1] [statement 2] [statement 3] [etc.]
不太理解?没关系,这里有一个例子创建了 Shape 的定义
- 代码示例 2 - 类的示例
#An example of a class
class Shape:
def __init__(self, x, y):
self.x = x
self.y = y
self.description = "This shape has not been described yet"
self.author = "Nobody has claimed to make this shape yet"
def area(self):
return self.x * self.y
def perimeter(self):
return 2 * self.x + 2 * self.y
def describe(self, text):
self.description = text
def authorName(self, text):
self.author = text
def scaleSize(self, scale):
self.x = self.x * scale
self.y = self.y * scale
你创建的是对形状的描述(即变量)以及你可以对形状进行的操作(即函数)。这非常重要 - 你并没有创建实际的形状,而只是创建了形状的描述。该形状具有宽度 (x)、高度 (y) 以及面积和周长 (area(self) 和 perimeter(self))。当你定义一个类时,不会运行任何代码 - 你只是在创建函数和变量。
名为 __init__ 的函数在创建 Shape 的实例时运行 - 也就是说,当我们创建实际的形状时,而不是我们这里的“蓝图”,__init__ 会运行。你稍后会明白它是如何工作的。
self 是我们在类内部从类内部引用事物的方式。self 是在类内部定义的任何函数的第一个参数。在第一个缩进级别(即代码行,这些代码行从我们放置 class Shape 的位置向右缩进一个 TAB)上创建的任何函数或变量都会自动放入 self。要在类内部的别处访问这些函数和变量,它们的名称必须以 self 和一个句点开头(例如 self.variable_name)。
我们可以创建类,但如何使用它呢?这里是一个创建“类的实例”的示例。假设示例 2 中的代码已经运行
- 代码示例 3 - 创建类
rectangle = Shape(100, 45)
做了什么?这需要一些解释...
__init__ 函数在此时真正发挥作用。我们通过首先给出它的名称(在本例中为 Shape)来创建一个类的实例,然后在方括号中,给出要传递给 __init__ 函数的值。init 函数运行(使用你在方括号中给出的参数),然后输出该类的实例,在本例中分配给名称“rectangle”。
将我们的类实例 rectangle 想象成一个自包含的变量和函数集合。就像我们在类内部从类内部使用 self 来访问类实例的函数和变量一样,我们现在使用分配给它的名称(rectangle)来从类外部访问类实例的函数和变量。接着我们上面运行的代码,我们这样做
- 代码示例 4 - 从实例外部访问属性
#finding the area of your rectangle:
print(rectangle.area())
#finding the perimeter of your rectangle:
print(rectangle.perimeter())
#describing the rectangle
rectangle.describe("A wide rectangle, more than twice as wide as it is tall")
#making the rectangle 50% smaller
rectangle.scaleSize(0.5)
#re-printing the new area of the rectangle
print(rectangle.area())
如你所见,在类实例内部使用 self 的地方,在类外部使用其分配的名称。我们这样做是为了查看和更改类内部的变量,以及访问那里的函数。
我们并不局限于一个类的单个实例 - 我们可以创建任意数量的实例。我可以这样做
- 代码示例 5 - 多个实例
long_rectangle = Shape(120,10)
fat_rectangle = Shape(130,120)
long_rectangle 和 fat_rectangle 都有自己的函数和变量包含在它们内部 - 它们彼此完全独立。我可以创建的实例数量没有限制。
面向对象编程有一套与之相关的术语。是时候把这些都弄清楚了
- 当我们第一次描述一个类时,我们是在定义它(就像函数一样)
- 将类似的函数和变量分组在一起的能力称为封装
- 单词“类”可以用来描述定义类的代码(就像定义函数一样),也可以用来指代该类的实例 - 这可能会让人困惑,所以请确保你知道我们指的是哪种形式的类
- 类内部的变量称为“属性”
- 类内部的函数称为“方法”
- 类与变量、列表、字典等属于同一类事物。也就是说,它们是对象
- 类被称为“数据结构” - 它保存数据以及处理这些数据的函数。
让我们回顾一下引言。我们知道类如何将变量和函数(称为属性和方法)分组在一起,以便数据和处理数据的代码都在同一个位置。我们可以创建该类的任意数量的实例,这样我们就不必为每个新创建的对象编写新的代码。但是如何向我们的高尔夫球杆设计中添加额外功能呢?这就是继承发挥作用的地方。
Python 使继承非常容易。我们基于另一个“父”类定义一个新类。我们的新类从父类中继承了一切,我们还可以添加其他东西。如果任何新的属性或方法与父类中的属性或方法同名,则使用它而不是父类中的那个。还记得 Shape 类吗?
- 代码示例 6 - Shape 类
class Shape:
def __init__(self,x,y):
self.x = x
self.y = y
self.description = "This shape has not been described yet"
self.author = "Nobody has claimed to make this shape yet"
def area(self):
return self.x * self.y
def perimeter(self):
return 2 * self.x + 2 * self.y
def describe(self,text):
self.description = text
def authorName(self,text):
self.author = text
def scaleSize(self,scale):
self.x = self.x * scale
self.y = self.y * scale
如果我们想定义一个新的类,比如一个正方形,基于我们之前的 Shape 类,我们会这样做
- 代码示例 7 - 使用继承
class Square(Shape):
def __init__(self,x):
self.x = x
self.y = x
这就像通常定义一个类一样,但这次我们在类名后面用方括号括起要继承的父类。如你所见,这让我们可以很快地描述一个正方形。这是因为我们继承了形状类的所有内容,并且只改变了需要改变的部分。在这种情况下,我们重新定义了 Shape 的__init__ 函数,以便 X 和 Y 值相同。
让我们从所学到的知识中汲取经验,创建一个新的类,这次继承自 Square。它将是两个正方形,一个紧挨着另一个的左侧。
- 代码示例 8 - DoubleSquare 类
# The shape looks like this:
# _________
#| | |
#|____|____|
class DoubleSquare(Square):
def __init__(self,y):
self.x = 2 * y
self.y = y
def perimeter(self):
return 2 * self.x + 3 * self.y
这一次,我们还必须重新定义 perimeter 函数,因为形状中间有一条线。尝试创建一个此类的实例。作为提示,IDLE 命令行从你的代码结束的地方开始 - 所以输入一行代码就像在你的程序末尾添加该行。
回想一下,当你声明一个变量等于另一个变量时,例如 variable2 = variable1,等号左侧的变量将采用等号右侧变量的值。对于类实例,这在某种程度上有所不同 - 左侧的名称将成为右侧的类实例。因此在 instance2 = instance1 中,instance2 指向 instance1 - 两个名称都指向同一个类实例,你可以通过任一名称访问该类实例。
在其他语言中,你使用指针执行类似的操作,但在 Python 中,所有这些都在幕后完成。
我们将要介绍的最后一件事是类字典。记住我们刚刚学到的关于指针的内容,我们可以将一个类的实例分配给列表或字典中的一个条目。这使得我们的程序运行时,几乎可以创建任意数量的类实例。让我们看下面的例子,并看看它是如何描述我的意思的。
- 代码示例 9 - 类字典
# Again, assume the definitions on Shape,
# Square and DoubleSquare have been run.
# First, create a dictionary:
dictionary = {}
# Then, create some instances of classes in the dictionary:
dictionary["DoubleSquare 1"] = DoubleSquare(5)
dictionary["long rectangle"] = Shape(600,45)
#You can now use them like a normal class:
print(dictionary["long rectangle"].area())
dictionary["DoubleSquare 1"].authorName("The Gingerbread Man")
print(dictionary["DoubleSquare 1"].author)
如你所见,我们只是用一个激动人心的、新的、动态的字典条目替换了我们无聊的旧左侧名称。很酷吧?