> For the complete documentation index, see [llms.txt](https://coding-girls-club.gitbook.io/python-turtle-tutorial/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://coding-girls-club.gitbook.io/python-turtle-tutorial/2-python-bian-cheng/2.4-ding-yi-lei-mi-gong/2.4.1-ding-yi.md).

# 2.4.1 定义

本节目标：

* 掌握如何定义类

## 类的定义

**Learning By Reading 难度：★ 重要性：★★★★★**

* 阅读[材料](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431864715651c99511036d884cf1b399e65ae0d27f7e000)

  ，了解定义类和类中的属性、方法的语法。

## 继承

> \[继承]\(<https://zh.wikipedia.org/zh-hans/继承_(计算机科学))是面向对象的一个基本概念，如果一个类別A「继承自」另一个类別B，就把这个A称为「B的子类別」，而把B称为「A的父类別」也可以称「B是A的超类」。> 继承可以使得子类別具有父类別的各种属性和方法，而不需要再次编写相同的代码。在令子类別继承父类別的同时，可以重新定义某些属性，并重写某些方法。

比如，“哺乳动物”类继承“动物”类，“人”类又继承“哺乳动物”类。

**Learning By Reading 难度：★ 重要性：★★★★★**

* 阅读材料，了解Python中的[继承](https://www.cnblogs.com/jason-lv/p/8325324.html)，

  以及[方法解析顺序](https://www.cnblogs.com/whatisfantasy/p/6046991.html)。

如果我们定义

```python
class MyTurtle(turtle.Turtle):

    _all_turtles = {}

    def __init__(self, *args, name="", **kwds): # args和kwds主要用来把除了name之外的参数传给turtle.Turtle的__init__，这样我们还是可以MyTurtle('turtle', name='ada')创建出一个小海龟形状的小海龟
        super(MyTurtle).__init__(*args, **kwds) # 很重要！MyTurtle继承了turtle.Turtle，而turtle.Turtle.__init__里面还做了许多其他事情
        self.name = name or "Ishmael" # 给小海龟起名字，如果没有指定名字……管我叫以实玛利吧！
        if self.name in MyTurtle._all_turtles:
            raise Exception("重名了！") # raise语句用来报错，你们曾经看过的各种错误都可以通过raise报出来。
        MyTurtle._all_turtles[name] = self

    def polygon(self, n, side):
        for i in range(n):
            self.fd(side)
            self.rt(360. / n)
```

那么这个`MyTurtle`就是一个继承了`Turtle`的类。 一个`Turtle`对象有的属性和方法，`MyTurtle`对象都有。 除此之外，`MyTurtle`还有一个`polygon`方法，画一个边长为`side`的正`n`边形。

这时候，我们

```python
my_turtle = MyTurtle()
my_turtle.polygon(42)
```

就画出了一个边长为`42`的正方形。

`MyTurtle`类有一个类属性`_all_turtles`用一个`dict`按名字存了所有小海龟。

```python
ada = MyTurtle(name='ada')
print(ada._all_turtles)
```

这样也打印出了所有小海龟。 Python发现对象`ada`没有`_all_turtles`这个属性，于是就会去`ada`所属的类`MyTurtle`去找有没有这个属性。

```python
ada = MyTurtle(name='ada')
ada._all_turtles = 42
print(ada._all_turtles) # 结果：42
print(MyTurtle._all_turtles)
```

对`ada`的属性赋值不会影响类属性。

## 注意类体与函数体的区别

类体中的代码会在定义的过程中执行，而函数体中的代码不会。

```python
def polygon(turtle, n, side):
    print("画一个多边形")
    for i in range(n):
        turtle.fd(side)
        turtle.rt(360. / n)
```

这段代码定义了一个函数`polygon`，但是函数中的`print`在定义的过程中没有被执行。

而

```python
class MyTurtle(turtle.Turtle):
    print("定义继承了Turtle的MyTurtle类")
    def polygon(self, n, side):
        for i in range(n):
            self.fd(side)
            self.rt(360. / n)
```

这段代码定义了一个类，类体中的`print`会被执行。

```python
def _polygon(turtle, n, side):
    for i in range(n):
        turtle.fd(side)
        turtle.rt(360. / n)

class MyTurtle(turtle.Turtle):
    polygon = _polygon
```

这段代码定义了函数`_polygon`，类`MyTurtle`，类中有方法`polygon`。 这时候，

```python
my_turtle = MyTurtle()

my_turtle.polygon(42) # 语句1
MyTurtle.polygon(my_turtle, 42) # 语句2
_polygon(my_turtle, 42) # 语句3
```

语句1、2、3是等价的。

```python
if MyTurtle.polygon == _polygon:
    print("相等！") # 会输出
```

会输出`相等！`。

同样的，

```python
turtle = Turtle()

turtle.fd(42)
Turtle.fd(turtle, 42)
```

两句话也是等价的。

在类体中定义的变量，都会变成类的属性和方法。

但是，在类体定义的变量，无法在外面使用。

比如，

```python
class MyTurtle(turtle.Turtle):
    master = "Your Name" # 类属性，小海龟主人名字
    def polygon(self, n, side):
        for i in range(n):
            self.fd(side)
            self.rt(360. / n)
print(master) # 报错！
print(MyTurtle.master) # 可以！
```

大家可能已经发现，函数中也有类似的情况。 这就牵涉到名称空间和作用域的概念了。
