# 第二章 变量和简单数据类型

## 概要

本章作者主要介绍了如何在 python 中操作变量，以及两类最基础的数据类型 —— 字符串和数字。下面我们来按顺序梳理一下相关的要点，以及延伸讨论一些有关 python 语言的基础知识。

## 关于变量

### 变量的命名建议

一个准确规范的变量名称有助于你在重读自己代码的时候更快理解（相信我，你会有这个需求的）。每一种编程语言都有它自己的规范风格，对于 python 来说，推荐你去快速浏览一下[这个页面的“命名”章节](https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/#id16)。

### 我是谁，我是不是谁，我在哪里

`a = 1` 是一行最最简单的 python 代码。但当你按下回车之后，python 解释器内部其实发生了很多事情。对于这些事情，我们可以提出哲学三问：

```
>>> a = 1
>>> b = '1'

# 我是谁
>>> type(a)
<class 'int'>
>>> type(b)
<class 'str'>

# 我是不是谁
>>> isinstance(a, int)
True
>>> isinstance(b, int)
False

# 我在哪里
>>> id(a)
4448371840
>>> id(b)
4451721776
```

我们来简单解释一下上面用到的三个 python 内置函数

* `type()` 告诉你某一个**变量**对应的**具体对象（object）**&#x662F;哪一个**类（class）**&#x7684;**实例（instance）**。
* `isinstance()` 告诉你某一个变量它是不是某一个类的实例。
* `id()` 告诉你这个变量所对应的具体对象（object）的**内存位置**。

### 为什么要知道这些呢？

让我们再重复一遍上面这句拗口的话：

> 某一个**变量**对应的**具体对象（object）**&#x662F;哪一个**类（class）**&#x7684;**实例（instance）**

这句话信息量其实很大，从这里我们可以引申出关于 python 的几个重要事实：

* **Python 中的一切都是对象（object）**。
* **Python 中的变量只是一个指针（pointer），或者说一个“名字”，它指向一个存放在内存中的具体对象。**
* **对象是类的实例**。

{% hint style="info" %}
那么什么是类？什么是对象？打个比方，类就是某一种类东西的定义，比如鸭子；而对象就是这个类的一个具体实例，比如唐老鸭和可达鸭就是具体的某只鸭子。**类是对象的模板。**
{% endhint %}

如果说牛顿三大定律构成了牛顿力学，那么**以上三条信息就构成了运行中的 python 世界**。当你运行一行简单的赋值代码如 `x = 2337` 时，实际上发生了下面这些事情：

1. python 在**内存的某个位置（id() 告诉你的那串数字）**&#x521B;建了一个对象，他的类型是 int。
2. python 设定这个 int 对象的值为 2337
3. python 又创建了一个叫做 `x` 的变量名
4. python 把 `x` 指向 1 中创建的那个 int 对象，那个 int 对象的 reference count 从 0 变为 1 （意思是有一个名字指向它，这个概念在内存回收中用到，对初学者不重要）。
5. 从此你可以用 `x` 这个变量名来获得 1 中创建的那个 int 对象

![图中等同于 x = 2337 这行代码](/files/-M6kxIQoUszUZ2O7dmEq)

我认为以上提到的这三条，是 python 知识体系中的主干，你将会看到的很多细枝末节的知识，都是从这些主干生长出来的。或许你仍然不太理解类和对象，继续看下去，我们将会在后面的很多章节反复深化这些东西的内涵。

## 一点点剧透：类与对象

本书的第九章会简单介绍类，而计算机专业课程中，更是有一门叫做面向对象编程（object oriented programming）的课程详细介绍类与对象的知识。但不要害怕，我们并不需要对这些掌握得面面俱到，在这里，我只想告诉你一些当前你所需要知道的关于类和对象的基础知识。

### 类/对象有属性（attribute）和方法（method）

```python
# 这里我给出定义一个鸭子类的完整代码，但是具体的语法解释在第九章，现在可以先不用细看
# 这里的重点是告诉你，什么是类的属性和方法
class Duck:
    def __init__(self, weight, color):
        # 鸭子的一些属性
        self.weight = weight
        self.color = color
    
    # 定义鸭子的一些方法
    def swim(lf):
        print('OK, swimming...')
    
    def fly(self):
        print("No I can't...")
    
    def introduce_myself(self):
        # 可以看到，鸭子的方法可以调用它自己的属性
        print(f'I am a {type(self)} with {self.color} color.')


# 实例化鸭子这个类，生成一个具体的鸭子对象
>>> duck_object = Duck(weight=1, color='yellow')

# 鸭子对象是鸭子的实例
>>> type(duck_object)
<class '__main__.Duck'>
>>> isinstance(duck_object, Duck)
True

# 调用鸭子的属性
>>> duck_object.weight
1
>>> duck_object.color
'yellow'

# 调用鸭子的方法，也就是让它完成一些事情
>>> duck_object.swim()
OK, swimming...
>>> duck_object.fly()
No I can't...
>>> duck_object.introduce_myself()
I am a <class '__main__.Duck'> with yellow color.

```

用上面的鸭子类来作为例子

* 属性就是一些固定的值，比如鸭子的体重（对应一个数字类型的变量）；鸭子的羽毛颜色（对应一个字符串，如 ‘yellow’）
* 方法就是一个能够被调用的函数，完成特定的事情，方法如同函数一样可以接受参数，方法内部可以使用属性

### 重新认识字符串

```
# 我把一个字符串对象赋值给变量 a，a 指向的是这个字符串对象，
# 字符串对象具有字符串类的全部属性和方法
>>> a = 'this two shall pass'

# 字符串对象是字符串类的实例
>>> type(a)
<class 'str'>

# 本章中提到的所有操作，其实都是在调用字符串对象的方法，这些方法都由字符串类这个模板定义
# 当你做这些操作时，你在调用字符串类的方法
# 这些方法帮你完成一些复杂的任务，没有他们的话，你需要写很多代码来实现同样的结果
>>> a.split(' ')
['this', 'two', 'shall', 'pass']
>>> a.upper()
'THIS TWO SHALL PASS'
>>> a.title()
'This Two Shall Pass'

# 当我定义一个数字类的时候， 它就具有不一样的属性和方法
>>> b = 42
# 数字类不存在 upper() 方法，因此我看到了一个 AttributeError
>>> b.upper()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'upper'

```

书中作者告诉了你很多关于字符串的操作方法（大小写，去除空白等等），**本质上，他只是挑选了字符串这个类的众多方法中，最常见和重要的几个**。而属于字符串类的方法和知识[还有很多很多](https://www.runoob.com/python3/python3-string.html)。你不妨浏览一下[这个链接](https://www.runoob.com/python3/python3-string.html)，但不需要一一记住，只要知道，许许多多事情是可以通过一个类的方法快速实现的。

{% hint style="warning" %}
超纲内容

如果你看上面那个链接，你会看到除了字符串方法外，还有很多字符串运算符的知识。比如 “+”，比如通过中括号和索引获得字符串的一部分 `"string"[2:4]。`这些语法似乎不像是类似`string.upper()` 这样的方法调用，但他们其实也是方法！

python 中有一些内置的方法，名称以双下划线包围，一般的初学教程不会提及。但如果使用`dir()` 这个函数，就可以看到所有的内置方法。其中`__add__()` 这个方法就实现了加号运算，有它的存在，你才可以使用加号连接两个字符串。加号只是这个特殊方法的一种简便写法！

```
# dir() 查看一个字符串，排第一的方法'__add__'就是加号的具体实现
>>> dir('')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
# 更有趣的是，dir() 本身也是在调用这个类的内置 __dir__() 方法才获得了上述信息
```

{% endhint %}

## python 中的一切都是对象

请记住，**一切**。Python 中，哪怕是 None ，都属于一个叫做 NoneType 的类，一样可以实例化并赋值给一个对象。上面的三条是普适的！

```
>>> a = None
>>> type(a)
<class 'NoneType'>
```

### 学习建议：学习 python 的用法就是学习 python 中各种类具有的属性和方法

正因为python 中的一切都是对象，学习某个 python 中的对象，也就成为了学习这个 对象/类 所具有的属性和方法。但是，千万别去死记硬背 string 类有哪些方法，每个方法有什么参数，有什么作用等等。你应该通过**不断的使用它来达到记忆的效果**。那些最常用的方法会自然成为你记得最深的方法。**编程是一个熟练功。**

与此同时，多 google，多看别人的代码，可以偶尔学到一些 **“原来这件事情可以通过这个方法完成”** 这样的知识。于是你就会在下一次遇到相同的问题时，用正确的“方法”去解决。

## 格式化字符串的方法之一：f-string （Bonus）

在编程过程中，你会经常需要把几个变量的值变成一句完整的话 print 出来，你有两种方式来做到这一点：

```
# 假如有关今天的日期信息存在几个不同的变量中，我们希望把这些变量变成一句话
date = '07'
month = 'May'
year = 2020
weekday = 'Thur'

# 方法一，用字符串加法，这是本书中的做法
>>> print('Today is ' + month + ' ' + date + ', '\
 + str(year) + ' ' + weekday + '.')
Today is May 07, 2020 Thur.

# 方法二，用 f-string 来实现字符串格式化
>>> print(f'Today is {month} {date}, {year} {weekday}.')
Today is May 07, 2020 Thur.
```

很显然，使用格式化字符串是更加 pythonic 的方法。而 f-string 是几种格式化字符串方法中最为**优美（程序员们喜欢用的高逼格词汇）**&#x7684;一种。

现在你知道了这个名词，就可以去 google 更多关于 f-string 的用法了。

## 看完之后，你应该能够

* 名词解释：类（class），实例（instance），对象（object）

```
           类
           |
           | （实例化）
           v
名称 --->  对象 （存在于内存中的具体数据，他的结构和行为都符合类的定义）
    (指向）

```

* 理解这些 python 内置函数：type()，isinstance()，id()
* 当你要学习或者搜索 python 中的字符串操作时，你实际在学习？
  * 答：字符串类的方法
* 死记硬背三大定理（以后慢慢理解）：
  * **Python 中的一切都是对象（object）**。
  * **Python 中的变量只是一个指针（pointer），或者说一个“名字”，它指向一个存放在内存中的具体对象。**
  * **对象是类的实例**。

## 拓展阅读

* 关于字符串方法更详细的罗列：<https://www.runoob.com/python3/python3-string.html>
* 关于 python 中指针的讨论：<https://realpython.com/pointers-in-python/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hq-1.gitbook.io/python/di-er-zhang-bian-liang-he-jian-dan-shu-ju-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
