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

概要

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

关于变量

变量的命名建议

一个准确规范的变量名称有助于你在重读自己代码的时候更快理解(相信我,你会有这个需求的)。每一种编程语言都有它自己的规范风格,对于 python 来说,推荐你去快速浏览一下这个页面的“命名”章节

我是谁,我是不是谁,我在哪里

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)是哪一个类(class)实例(instance)

  • isinstance() 告诉你某一个变量它是不是某一个类的实例。

  • id() 告诉你这个变量所对应的具体对象(object)的内存位置

为什么要知道这些呢?

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

某一个变量对应的具体对象(object)是哪一个类(class)实例(instance)

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

  • Python 中的一切都是对象(object)

  • Python 中的变量只是一个指针(pointer),或者说一个“名字”,它指向一个存放在内存中的具体对象。

  • 对象是类的实例

那么什么是类?什么是对象?打个比方,类就是某一种类东西的定义,比如鸭子;而对象就是这个类的一个具体实例,比如唐老鸭和可达鸭就是具体的某只鸭子。类是对象的模板。

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

  1. python 在内存的某个位置(id() 告诉你的那串数字)创建了一个对象,他的类型是 int。

  2. python 设定这个 int 对象的值为 2337

  3. python 又创建了一个叫做 x 的变量名

  4. python 把 x 指向 1 中创建的那个 int 对象,那个 int 对象的 reference count 从 0 变为 1 (意思是有一个名字指向它,这个概念在内存回收中用到,对初学者不重要)。

  5. 从此你可以用 x 这个变量名来获得 1 中创建的那个 int 对象

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

一点点剧透:类与对象

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

类/对象有属性(attribute)和方法(method)

# 这里我给出定义一个鸭子类的完整代码,但是具体的语法解释在第九章,现在可以先不用细看
# 这里的重点是告诉你,什么是类的属性和方法
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'

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

超纲内容

如果你看上面那个链接,你会看到除了字符串方法外,还有很多字符串运算符的知识。比如 “+”,比如通过中括号和索引获得字符串的一部分 "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__() 方法才获得了上述信息

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 是几种格式化字符串方法中最为优美(程序员们喜欢用的高逼格词汇)的一种。

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

看完之后,你应该能够

  • 名词解释:类(class),实例(instance),对象(object)


           |
           | (实例化)
           v
名称 --->  对象 (存在于内存中的具体数据,他的结构和行为都符合类的定义)
    (指向)
  • 理解这些 python 内置函数:type(),isinstance(),id()

  • 当你要学习或者搜索 python 中的字符串操作时,你实际在学习?

    • 答:字符串类的方法

  • 死记硬背三大定理(以后慢慢理解):

    • Python 中的一切都是对象(object)

    • Python 中的变量只是一个指针(pointer),或者说一个“名字”,它指向一个存放在内存中的具体对象。

    • 对象是类的实例

拓展阅读

最后更新于