Skip to content

Latest commit

 

History

History
1085 lines (867 loc) · 36.8 KB

File metadata and controls

1085 lines (867 loc) · 36.8 KB

1. Python简介


学习先导:

  • 背景:了解Python语言基本背景。 Python是什么?它从何而来?当前有何应用?相比其他语言的优劣势?计算机语言的未来如何发展?
  • 组成:理解Python语言的基本组成。 Python程序是如何构造出来的,他们的每一块分别都一般由什么组成?而组成这万千大厦的基本单位是什么?
  • 存储:理解基本数据类型和存储方式。 我们可以指定给Python解释器的基本数据类型有哪些,如何结构化的保存这些数据?
  • 交互:体验及了解如何与Python程序进行数据交互。 理解最简单的数据交互方法,并对此后的数据传参有一个基本印象。
  • 基本操作:理解 “变量”、“对象”、“赋值绑定” 三大概念。 理解对象及面向对象编程,及如何暂存数据。

Ps: 第一章拥有仅教程不到20%的内容,但却拥有着80%的重要程度。
可以说,第一章理解了,此后内容都是对此的延展。

思维导图:

<iframe frameborder= "no" border= "0" marginwidth= "0" marginheight= "0" width=800 height=800 src= "https://edrawcloudpubliccn.oss-cn-shenzhen.aliyuncs.com/viewer/self/21594733/share/2021-1-12/1610454561/main.svg"></iframe>

1.1. Python背景

Python是什么?
Python is a programming language that lets you work quickly and integrate systems more effectively.

  • 一门高级编程语言
  • 面向对象
  • 动态语言
  • 支持多种编程范式

1.1.1. 诞生

  • 诞生时间: 1989圣诞节期间
  • 创始人: Guido van Rossum(荷兰)
  • Python的命名: 源于一个喜剧团Monty Python
  • Python的官网: <www.python.org>
  • Python的版本:
    • Python v2.7(2020年结束维护)
    • Python v3.5(当前学习环境)
    • Python v3.8(最新版本)
  • 目的
    • 功能强大,与其他主要编程语言一样(当时C语言独霸天下,使用占比超50%,后来最高70%+)
    • 清晰而优雅,尽量直接使用直译的英文单词以便于理解
    • 简洁高效开发,并为大量非专业程序员提供日常轻量级使用
    • 开源

小知识:
Python出现的历史大背景:(编程语言的发展及Python历史由来) 按照编程语言发展类型排序:

  • 机器语言(0/1组成)
  • 汇编语言(英文单词代替一些0/1组成)
  • 高级语言(面向流程 --> 面向对象)

计算机语言发展趋势及特点:

  • 人机交互越来越简单。
    • 语法愈发简洁。
    • 交互模式愈发人性化。如动态数据类型

1.1.2. 应用

  • 应用领域:
    数学(最早)、系统运维、网络编程、科学计算、人工智能,机器人、web开发、大数据及数据库编程、云计算、教育、游戏、图像、其他

1.1.3. 关于这门语言的评价

  • Python优缺点:
    • 优点:
      1. 面向对象(Java,C++,Python)
      2. 免费
      3. 简单易学
      4. 可移植
      5. 可混合编程(C/C++/Java/.net)
      6. 开发效率高
      7. 开源
    • 缺点:
      1. 和C/C++相比执行速度不够快(最高的是C,其次C++)
      2. 不能封闭源代码

执行效率vs开发效率,两者几乎不能兼得。
所以涉及效率的部分可以C/C++开发,这种特性使得Python被称为胶水语言。

1.1.4. 版本与兼容区别

Python大版本向下兼容小版本,大版本不兼容。如 Python3.7 兼容 Python3.6, Python3.x 不兼容 Python2.x

1.1.4.1. Python3比Python2的优势

一些示例:

  • 数值计算更符合数学思维:

    如: 整数相除中,“/”在python2中是地板除,python3中则是数学意义上的除法  
      python2: 1/2=0  
      python3: 1/2=0.5  
  • 数据处理速度更快:

    实例: 实测迭代300M大小的文件
    测试方法: 读取文件,for循环迭代每一行,再用for循环迭代每一个字符,pass填充语句
    结果: 
    python2平均花了20秒,二进制流花了17秒
    python3平均花了13秒,二进制流花了11秒
  • 对于一些数值类型定义更明确:
    python3会尽量减少一些BUG,如数值类型比较:

    $ python2
    >>> 1000 < 1500 < 2000
    True
    >>> 1000 < "1500" < 2000
    False
    
    $ python3
    >>> 1000 < 1500 < 2000
    True
    >>> 1000 < "1500" < 2000  # 主动抛出异常,避免人为错误
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: '<' not supported between instances of 'int' and 'str'

    ...


1.2. Python的安装及环境搭建

1.2.1. Python 的安装:

Windows / Mac OS X / Unix ...

1.2.2. Python的解释执行器

CPython (Python)    ——C写的
Jython              ——Java写的
IronPython          ——.net
Pypy                ——Python

Ps, 了解编译解释基本原理:

  1. 高级语言 --(转换为)--> 机器语言,可带来不同平台的兼容性

1.3. Hello world: Python的运行

1.3.1. 在Linux中Python执行的方式

方法一: (交互模式)命令行交互提示模式

  • 打开命令窗口, 输入python进入交互模式:

    $ python3
    >>> print("hello")
    >>> quit()    #退出交互模式,exit(),或 Ctrl + D

方法二: (脚本模式)调用系统python程序执行文件

  • 方式1: 命令行指定系统程序调用,去运行脚本文件(文件外去指定python程序运行脚本)

    step1. 编写文本文件如下,存储为文件: hello.py

    print("hello world!")
    print("today is friday!")

    step2. 随后在同级目录下打开命令窗口运行命令:

    $ python3 hello.py
  • 方式2: 脚本文件头指定调用程序路径,直接运行脚本(将文本文件直接作为执行文件,第一句写入解释语句,指定调用程序,该方法一般用于Linux下)

    step1. 编写文本文件如下,存储为: hello.py

    #! /usr/bin/python3
    print("hello world!")
    print("today is friday!")

    step2. 随后在同级目录下打开命令窗口运行命令:

    $ chmod a+x hello.py   #给文件增加可执行权限。
    $ ./python.py		#执行程序。要求文件头里须有解释语句 #! /usr/bin/python3
    一般而言常用运行方式有:
    1.Linux/Win-cmd/Win-PowerShell/Other等命令行窗口运行脚本;
    2.第三方软件sublime, vscode, pycharm等运行脚本。
    (他们本质皆为调用Python解释执行器)

1.4. Python程序的基本构成

程序由模块组成
模块由语句,函数,类,数据等组成
语句包含表达式
表达式建立并处理数据对象

包含关系: ( -> 模块) -> ( -> 方法)(函数) -> 语句块 -> 语句 -> 表达式 -> 数据对象)


此包含过程可与书籍类比:

  • 程序 ---- 全书
  • 包 ---- 第几部分
  • 模块 ---- 章
  • 方法(函数) ---- 节
  • 语句块 ---- 段
  • 语句 ---- 句
  • 表达式 ---- 短语
  • 数据对象 ---- 基本单词

常见的单词:

类型 英语 基本功能描述
模块 import 导入模块
class 定义类
函数(类方法) def 定义函数
语句块(循环) for 开始一个循环
语句块(循环) while 开始一个循环
语句块(流程控制) if 逻辑流程判断
语句(循环) break 循环终止
语句(循环) continue 跳至下一个循环
语句 pass 空语句,什么也不做
表达式 and 且计算
表达式 or 或计算
表达式 is 对象判断
表达式 in / not in 对象存在查找判断
... ... ...

示例: Python结构组成的基本示例 demo_python_structure

1.4.1. 语句和表达式

语句和表达式是Python中连接一切的关键,上至语句块的封装(函数、类、模块),下至基本数据对象的创建、存储及运算。那么,如何来理解这两个基本概念?

语句和表达式基本概述

  • 表达式 expression: 表达式,是由数值、运算符、分组符号(括号)、自由变量和约束变量等以能求得数据对象的有意义排列方法所得的组合。
    ( 建立或操作数据对象的句子, 如 1, 1 + 3, sum([1, 2, 3])等 )

    表达式建立并操作数据对象;
    表达式运算完毕返回运算的最终结果,该结果可赋值给变量。例如: 1+2+3就是表达式,运算完毕最终返回6,可赋值给变量;
    若计算结果值未赋值给变量,该行执行完后返回得到的数据对象一般将在内存销毁。

  • 语句 statement: 由表达式、运算符、变量等组成的执行某件事的完整句子
    (执行完整功能的句子, 如i = 1 + 3, a = sum([1, 2, 3]), pass等)

    语句一般可以这样理解,语句:变量 = 表达式
    当然一些功能性语句不满足上式:如pass/break/continue等功能性操作句子也是语句
    表达式运算后结果能赋值给变量,语句则不能(可利用这一点测试并区分表达式和语句)

Python3中哪些内容被称为表达式,哪些是语句?

  • 表达式: 算式,数值对象(数值、字符串、列表等)的定义和创建,函数的调用,类的调用等。
  • 语句: 赋值语句,特殊功能性语句。
  • 表达式与语句的区别? 表达式可以变为语句,而语句不能再参与操作和计算。可以这样理解:表达式可以作为一个单独的语句,但语句却不一定能作为一个单独的表达式。
  • 区别表达式与语句的简单方法: 进入python交互模式界面。输入a=测试句子print(测试句子),按Enter后能正常运行的都是表达式,如果不能正常运行的,则为语句。表达式一般都有返回值,如函数至少返回为None。(后者调用函数的实质也为赋值,赋值的前提是有数据对象返回。)

示例1:表达式和语句简单区别方式

以下内容哪些是表达式,哪些是语句?

12.5
10+2.5
"hello"
[1,2,3]
list
print()
i=10+2.5
pass

仅使用print测试(使用a=测试句子测试也一样,函数传参其实也类似于赋值操作):

print(12.5)     # 表达式。创建数值对象,返回该数值对象
# 12.5
print(10+2.5)   # 表达式。创建两个数值对象,运算完毕返回最终结果对象
# 12.5
print("hello")  # 表达式。创建字符串对象,返回该字符串对象
# hello
print([1,2,3])  # 表达式。创建列表对象,返回该列表对象
# [1, 2, 3]
print(list)     # 表达式。list类对象(Python自带的类对象),返回类对象类型
# <class 'list'>
print(print())  # 表达式。函数print()对象,该函数运算结束返回None
# None
print(i=10+2.5)  # 完整语句
# Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
# TypeError: 'i' is an invalid keyword argument for print()
print(pass)      # 完整语句
#   File "<stdin>", line 1
#    print(pass)
#            ^
# SyntaxError: invalid syntax

结果:只有最后两个是语句,其余都是表达式


示例2:对于函数表达式的深刻理解

以下式子打印结果是什么?

>>> print(print(666))  # 打印结果是什么?

语句,赋值,表达式拆解的深度理解:

# 1)print函数
# 函数本身执行一次,返回的None再通过print打印一次
>>> print(print(666))
666
None
# 如何理解以上print()? 可以将以上式子拆解如下进行理解:
# 表达式print(666)运算,会打印666,最终创建返回None对象,
# 随后将运算后结果赋值给a
>>> a = print(666)
666
>>> print(a) # 之后再通过一次print打印a的值
None

# 2)表达式创建None对象,返回None对象,随后赋值给a2
>>> a2 = None

# 3)创键300、2000对象,运算完返回2300数值对象,随后赋值给b1
>>> b1 = 300 + 2000

# 4)创键300、1000、1000对象,运算完返回2300数值对象,随后赋值给b2
>>> b1 = 300 + 1000 + 1000

第一句和第二句实际是等效的,最终创建并返回None对象,只是做的过程不同。
第三句和第四句实际也是等效的,最终运算完创建并返回2300对象,只是做的过程不同。
因而,仅仅看过程上,print()和创建None,运算1+2都一样,都是表达式进行运算的过程,运算完毕最终返回某个结果。

1.4.2. Python的中的各种标识符号

  • 常见标识符:
符号 说明
# 注释一行,#开始到行尾。作用: 让注释内容不参加程序解释执行
\ 语句末尾的\表示语句换行符;字符串中的\常表示转义符
; 语句分隔符
: 语句块的开始
, 数据对象分割符
( ) 元组类型表示;数据对象之间运算的优先级表示;还常用于隐式换行
[ ] 列表类型表示
{ } 字典类型、集合类型等表示
' '" " 字符串类型表示
''' '''
""" """
字符串类型表示,多行注释。常用于函数、类、模块等开头说明文档
  • 常见运算符
符号 说明
= 赋值运算符
> < == <= >= 比较运算符
+ - * /
// %
**
算术运算符,数值计算运算符
+= -= *= /=
//= %=
**=
赋值运算符
& ` ^ ~ << >>`
in not in 成员运算符
...
  • 关于Python中的注释
    1. 单行注释
      #开始的位置一直到行末不再被编译器作为代码读入,用于人为注解标识。 如:

      # 注释内容1,这段代码是做路径距离计算事件的
      a = 0      # 注释内容2,这一小步是做数据初始化事件的
      a = a + 2  # 注释内容3,这一小步是做一次步进事件的
      a = a + 3  # 注释内容4,这一小步是做又一次步进事件的
    2. 特殊注释
      一般文件前两行为特殊注释:

      第1行为解释执行器路径
      第2行为当前文件的编码
      如:

      #! /usr/bin/python3
      # -*- coding=utf-8 -*-
    3. 多行注释
      '''"""一对三单引号的开始和结束的区域,作为注释内容。
      当放于文件、函数、类等开始时,常被用作help文档,如模块(文件)文档,类文档,函数文档。
      如numpy的包初始化文件python\lib\site-packages\numpy\__init__.py

      """
      NumPy
      =====
      
      Provides
        1. An array object of arbitrary homogeneous items
        2. Fast mathematical operations over arrays
        3. Linear Algebra, Fourier Transforms, Random Number Generation
      ...
      """

      通过help(numpy)便会在DESCRIPTION中将该文档显示出来

  • 关于Python中的换行:
    • 换行规则

      一般一条语句一行;
      多条语句在一行中用“;”分隔(一般不采用,需遵循PEP8规则);
      一条语句的换行在多行中用“\”连接,或在“括号”中进行单或多次换行。
      
    • 一条语句的换行
      \,可实现显式换行,实现语句连贯
      ()[]{},可实现隐式换行,实现语句连贯

      # 显式换行
      a, b = 1, \
             2
      print(a, b)  # 1 2
      
      # 隐式换行
      a, b = (1,
              2)
      print(a, b)  # 1 2

1.5. Python中数据存储、运算与基本交互

1.5.1. Python中的基本数据类型及表示

茫茫大海,计算机是为处理、存储和计算信息而服务,那么Python中的数据又如何分类存储并计算呢?

Python3 中有六个标准的基本数据类型 :

  • Number(数值: 整数int, 浮点数float,复数complex,布尔型bool
  • String(字符串: str
  • List(列表: list
  • Tuple(元组: tuple
  • Set(集合: setfrozenset
  • Dictionary(字典: dict

其中,

  • 不可变数据对象(4 个): Number(数值)、String(字符串)、Tuple(元组)、frozenset固定集合
  • 可变数据对象(3 个): List(列表)、Dictionary(字典)、Set(集合)。

除数值对象外,以上类型都属于可迭代对象(iterable)

>>> 1, 1.0, 1+0j, bool(1), "1", [1], (1,), {1}, {1: 1}
(1, 1.0, (1+0j), True, '1', [1], (1,), {1}, {1: 1})

补充:

  • 可变对象的区别方法为: 在创建的地址不变情况下,判断对象内是否有元素可以发生变化。
  • 字符串、列表、元组被合并称为序列, 所谓序列, 即成员有序排列, 可通过下标访问,切片。

1.5.2. Python中的基本数据运算

基本操作,可以有:

  • 基本数据赋值创建
  • 基本数学运算
  • 数据容器的增删改查

宏观层面,可以有:

  • 函数的调用
  • 类的实例化
  • 类方法调用
  • 模块的使用

1.5.3. Python内置函数之基本“输入”、“输出”

标准输入输出的处理

  • 基本输入函数: input()

    • 功能:从交互界面获取用户键盘输入内容,或用于Linux管道的输入。

    • 返回:str。返回输入的字符串,自动删除末尾回车生成的\n。(若要保留字符,请见后续模块一章sys模块的使用)

    • 格式及用法:

      a = input("自定义输入提示语(可空)")
      print(a)

      linux高级玩法(了解):

      echo 123|python3 <(echo 'print(input("hello"))')
      # hello123
    • 使用方法:交互界面输入内容,按回车结束,此时系统自动捕捉输入内容(字符串)

  • 基本输出函数: print()

    • 功能:打印显示表达式运算结果于屏幕。如果是内容是一个表达式(如函数),则打印其“返回结果”。

    • 返回:None

    • 格式:

      print(a , b , c , … , sep=" ",end="\n")
      • a, b, c, ...: 为表达式(包括数字、字符串等)
      • sep: 为输出间隔字符,默认为空格,可自定义
      • end: 为末尾默认字符,默认为回车,可自定义

小知识:

在交互模式下,print和表达式都会打印结果,两者有什么区别? 如何理解print和表达式中的打印?
如:

>>> 'hello'  # 此为表达式的返回值可视化
'hello'
>>> print('hello')  # 此为print()函数的功能性打印
hello

>>> a='hello'  # 表达式的返回值被赋值到了a也就不打印了
>>> a=1+1; 2-1; 4-1  # 只可视化最后一个数据对象的返回值
1
  • “表达式打印” 是交互模式下,Python解释器所特有的功能,它为逐行执行与调试而设计,将即将在内存z。表达式操作数据对象,一般表达式的返回值结果在未绑定变量时则打印表达式处理后预返回的基本结果信息,当放在脚本执行时将不会打印。
  • “print()打印” 是一个函数执行功能,是一个功能语句,放在脚本执行仍然执行打印操作。 如图


    ——(摘自Python小课团队制作图片)

1.6. 对象、变量、赋值的基本概念

1.6.1. 对象——Python程序的基本单位

Python中一切皆对象,如运算符等各种标识符,基本数据类型:数值,字符串,元组,列表,字典,集合等(他们本质上也是被封装的函数或类),函数,类等等,当然,也可以自定义函数或类。

通过 help(对象) / help("对象") 可以查看该对象的基本属性。

如: help("(")help("list")help("sum")

1.6.2. 变量——用于绑定对象的名字对象

  • 变量基本概念:
    变量是用来绑定数据对象的标识符,变量可以绑定任意表达式结果。

  • 作用:
    在内存中用于标识暂存数据以便后续使用。具体原理可了解对象计数指针。

  • 变量的数据类型:
    变量的数据类型实质是绑定的对象的数据类型。它是动态的,随绑定对象变化。
    查看变量类型的函数 type(x)

1.6.2.1. 变量的定义

  • 定义变量: (先定义后使用)

    1. 取名。在变量命名规则有效范围内,进行有规范的命名,如驼峰命名法。
    2. 赋值创建: 第一次赋值时,创建的变量与对象进行“赋值绑定”。
  • 变量命名规则

    取名规则:

    变量名必须是标识符: [A-Za-z_][A-Za-z0-9_]*

    1. 开头只能是下划线or字母
    2. 由字母or下划线or数字构成
    3. 大小写不同
    4. 保留关键字(PEP8规则详细描述了取名规则,文件排版等规则,包含其他书写规则见官网文档,或附件PEP8编码规范。一般在日常使用中都用编译器相关插件完成一键排版)

    合法的变量名示例:
    a a1 b bbb _aaa_ _Abcdvara1b2c2

    不合法的变量名示例:
    1a 123 $ABC +a -b #ab @ab

    Python 的关键字: ( help("keywords") 可以查看已经使用的关键字)
    True, False, None, is, del, if, elif等


Ps:
Python3相较于Python2改进了编译器,可以使用汉字作为变量名,但尽量不要使用,因为保存的py文件编码方式不同,可能造成打开文件乱码(当然,也可以文件头指定文件编码方式)。
变量定义尽量保证是英文,中文符号、特殊字符等尽量存在于字符串或注释。

1.6.2.2. 变量的删除(del函数解除变量绑定)

解除变量绑定,一般当引用计数为零时销毁其绑定对象。
原理:Python内存管理和引用计数(为零时,对象删除)

>>> dir()
[..., '__name__', '__package__', '__spec__']
>>> a = 2000
>>> dir()
[..., '__name__', '__package__', '__spec__', 'a']
>>> del a
>>> dir()
[..., '__name__', '__package__', '__spec__']

1.6.3. 赋值——用于“绑定对象与变量”的操作

  • 赋值(“绑定”)概念:
    赋值/关联/绑定/引用: 变量和一个数据对象进行关联关系,从数据对象到变量进行单向绑定。

  • 赋值绑定作用
    暂存数据。每行语句的表达式都会单独生成对象,若数据对象没有被变量绑定则运行完此语句可能销毁。

  • 赋值格式:
    变量名 = 表达式建立的对象
    ( "=" 是赋值运算符 )

  • 其他变量赋值方式:

    1)多重赋值

    a = a1 = a2 = a3 = 123  # 多重赋值
    print(a, a1, a2, a3)  # 123 123 123 123

    2)多元赋值
    (更多详情见后续章节中,数据容器-->序列的多重赋值)

    a, a1, a2, a3 = 1, 2, 3, 4
    print(a, a1, a2, a3)  # 1 2 3 4

    3)增量赋值

    x += 1  # x = x + 1
    x -= 1  
    x *= 2  
    ...

小知识:

变量互换: x, y = y, x

# 多元赋值交换变量方式不需要创建新的变量对象
a, b, c = 1024, 2048, 4096
print(id(a), id(b), id(c))  # 2210734229392 2210762967664 2210734229520
b, a, c = a, b, c 
print(id(b), id(a), id(c))  # 2210734229392 2210762967664 2210734229520

# 经典交换算法: (该方法会创建变量新对象)
a, b = 100, 200
temp = a
a = b
b = temp

1.7. 认识事物本质——探索对象、变量及赋值的本质的方法

1.7.1. 如何查看帮助文档?

Python编程中非常重要的一个本领,查看帮助文档,该方法连接了不同人之间高效交流的桥梁。

  • help(x)函数
help(x)
# 可以查看关于x的信息,x可以是函数名、类名、类方法、标识符、特殊语句等
# 如: help(print)  # 可查看函数传参属性,如end="\n"

常用:
help(函数名)  # 可以查看函数帮助文档,如help(print),help(pow)
help(类名)    # 可以查看类的帮助文档,如help(list)
help(模块)    # 查看模块的帮助文档
help(对象.方法)  # 查看某个方法文档

示例:
help("(")     # 查看逗号使用描述
help("list")  # 查看列表类对象使用帮助
help("sum")   # 查看函数sum使用

其他用法:
help("keywords")  # 查看所有关键字
help("modules")   # 查看所有可用模块
help("topics")    # 查看常见的topics
help("__main__")  # 查看当前文件帮助文档
  • pydoc程序
$ pydoc -g  # 生成html帮助文档报表

1.7.2. 如何查看环境中的变量?

  • 函数查看
help("__main__")  # 返回包含变量关系的帮助文档字符串
dir([对象])  # 返回指定对象内(默认为当前环境变量)定义的名字(属性,方法,)的列表
globals()   # 返回当前全局作用域内变量的字典
locals()    # 返回当前局部作用域内的变量的字典
  • 类属性查看 (暂作了解)
    类.__dict__ 查看类对象实例的变量绑定关系,__dict__属性绑定一个存储此实例自身变量的字典

  • 常见系统变量 (暂作了解)
    __name__ 当前执行模块(python程序文件)的主名称,一般当前主模块为"__main__",使用的其他变量为变量名。

示例:

  1. help("__main__") 查看运行所有的变量,绑定关系,及其说明文档的方法
    编辑py文件: test.py

    """
    help: 
    ********test doc自定义文档********
    """
    
    a = 2
    print('当前模块名:', __name__, "类型是", type(__name__))
    print(help(__name__))

    执行:

    $ python3 test.py
    当前模块名: __main__ 类型是 <class 'str'>
    Help on module __main__:
    
    NAME
        __main__
    
    DESCRIPTION
        help: 
        ********test doc自定义文档********
    
    DATA
        __annotations__ = {}
        a = 2
    
    FILE
        c:\\users\\jun\\desktop\\test.py
    
    None

2、dir([对象]) 查看变量名方法

  • 作用:
  1. 如果没有参数调用,则返回当前作用域内所有变量的列表
  2. 如果给定一个对象作为参数,则返回这个对象的所在变量(属性)列表
    1. 对于一个类对象,返回类对象的所有变量,并递归基类对象的所有变量
    2. 对于一个模块,返回这个模块的全部变量
    3. 对于其它对象,返回所有变量、类变量和基类变量

示例:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> dir(list)
[..., 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> L = [1,2]
>>> dir(L)
[..., 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
  1. locals() / globals()

环境变量的本质其实也是一个字典(了解),如:

print(locals().keys())
a = 1
print(locals().keys())  # 比前一次多了 'a'
# 直接对环境变量的字典修改
locals().update({'b': 2})
print(locals().keys())  # 比前一次多了 'b'
print(b)  # 可以直接使用变量b

运行结果:

dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__'])
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'a'])
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'a', 'b'])
2

1.7.3. 如何查看对象基本属性?

  • type(obj) 查看对象所属类型
  • id(obj) 函数查看对象内存地址代号
  • is 判断是否是同一个对象

1.7.3.1. type(obj) 查看对象所属类型

返回对象的类型
基本类型都可以用type(obj)判断:

>>> type(123)  # 数值
<class 'int'>
>>> type('123')  # 字符串
<class 'str'>
>>> type(None)   # None对象
<type(None) 'NoneType'>
>>> type([123])  # 列表
<class 'list'>
>>> type((123,)) # 元组
<class 'tuple'>
>>> type({123: None})  # 字典
<class 'dict'>
>>> type({123})  # 集合
<class 'set'>
...

1.7.3.2. id(obj) 函数查看对象内存地址代号

Python对象有唯一标识。函数id()可查看返回对象的内存地址的代号(虚拟环境,非真实地址编号,不同于C语言)。
可用于查看对象存储于计算机内存的“地址”,以精确判断对象的存储性质。

示例:

# 交互模式下运行
id(1)  # 4457501824
id(2)  # 4457501824
id(256)  # 4457509984
id(257)  # 140662152355888
id(1000)  # 140662152355888

1.7.3.3. is判断是否是同一个对象

is判断: 判断变量或对象是否为同一个对象

  • is,如 a is b
  • is not,如 a is not b

a is b实际等效于id(a) == id(b)

为什么需要判断?表面值相同在内存中的数据对象不一定相同。


示例1: 探索变量相同表面值在内存中是否为同一对象

# 以下代码在交互模式下执行
a, b = [], []
a == b  # True  # 字面值相同
a is b  # False # 却发现他们不是同一数据对象
print(id(a), id(b))  # 1896198034248 1896198037448
# 发现他们在内存中的id不同,它们分别在不同位置新建列表对象

示例2: is判断某个实例对象是否是某个类型

# 以下代码在交互模式下执行
type(20) is int  # True
type(20) == int  # True
type([3, 5, 8]) is list  # True

type("a")  # <class 'str'>
str  # <class 'str'>
type('abc') == str  # True
type('abc') is str  # True

class A(): pass
type(A()) # <class '__main__.A'>
A # <class '__main__.A'>
type(A()) is A  # True
type(A()) == A  # True

示例3:探索“小整数对象池”在内存中的存储规则: 小整数对象池:Python程序创建之初,便初始化-5到256的数在内存中,它们随程序运行周期永远存在,不会释放,且单份存在(在这个范围内不会创建新的范围内的数值对象)。
也就是说,大于或小于该边界的数值对象创建会重新创建新对象。

# 256的id号重新创建赋值不会改变,并且自始至终只有一个"256"对象
>>> a, b = 256, 256
>>> print(a==b, a is b, id(a), id(b))
True True 140705097695600 140705097695600
>>> del a,b
>>> a, b = 256, 256
>>> print(a==b, a is b, id(a), id(b))
True True 140705097695600 140705097695600

# 257的id号重新创建赋值会改变,会创建新的"257"对象
>>> a, b = 257, 257
>>> print(a==b, a is b, id(a), id(b))
True False 2767442848048 2767442849200
>>> del a,b
>>> a, b = 257, 257
>>> print(a==b, a is b, id(a), id(b))
True False 2767442849648 2767442847696
# 4个257id都不同

1.7.4. 理解对象赋值变量的动态关系——赋值过程

赋值(绑定)本质

分别创建“变量对象”“数据对象”,从“数据对象”“变量对象”单向赋值绑定。

如:
在赋值操作a=1000; b=1000的过程中,Python解释器大概做哪几个动作?

  1. 数据对象准备,检测内存中1000非系统必要保留对象,创建“对象1:1000”,创建“对象2:1000”
  2. 创建变量“变量对象3:a”“变量对象4:b”
  3. “对象1:1000”“变量对象3:a”单向绑定,从“对象2:1000”“变量对象4:a”单向绑定,最终“a”、“b”分别绑定到了两个1000对象上。

在赋值操作a=10; b=10的过程中,Python解释器大概做哪几个动作?

  1. 数据对象准备,检测内存中有“10”属于小整数对象池,属于系统必要保留对象(无需创建)
  2. 创建变量“变量对象3:a”“变量对象4:b”
  3. 从“10”到“a”单向绑定(联系),从“10”到“b”单向绑定,最终“a”、“b”都绑定到同一个对象“10”

深入理解

问题一: 多重赋值的本质是什么?
例如:c = b = a = [10]一式运行后,最后 a, b 和 c 都指向内存中的哪个数据对象呢?

都指向了绑定的数据对象[10]

变量本身不保存任何数值。变量向另一个变量赋值(绑定),实际过程是:当前变量查找去赋值的变量所对应的数据对象,然后将对应数据对象与当前变量进行赋值。

由于赋值的单向性,多个变量可绑定到一个数据对象,而一个变量只能绑定一个数据对象

c = b = a = [10] 等价于 a = [10]; b = a; c = b,即a绑定到[10],b绑定到a,c绑定到b。

c = b = a = [10]
# 以上一式等效于以下三式:
a = [10]
b = a
c = b

"""赋值绑定过程:
a = [10]
  数据对象[10]
   / 
  a  

b = a
  数据对象[10]
   / |
  a  b

c = b
  数据对象[10]
   / | \
  a  b  c
"""

# 验证:当数据对象从[10]增加为[10, 20],
# 其他变量打印结果都改变,但最终他们的id不变。

>>> c = b = a = [10]
>>> print(a, b, c, id(a), id(b), id(c))
[10] [10] [10] 1896198013512 1896198013512 1896198013512

>>> a += [20]
>>> print(a, b, c, id(a), id(b), id(c))
[10, 20] [10, 20] [10, 20] 1896198013512 1896198013512 1896198013512

问题二: 如何理解对象的创建与销毁?
在赋值操作a=1000; a=2000; a=1000的过程中,1000是否会被销毁?

会,在第二次赋值时,对象1000的引用计数从1变为0,非系统必要保留数值对象被销毁。

a = 1000
a = 2000  # 对象1000是否被销毁?
a = 1000  # 对象2000是否被销毁?
"""赋值绑定过程:

a = 1000
  数据对象1000
   /
  a

a = 2000
  [x]数据对象1000[x]  数据对象2000
                     /
                    a

a = 1000
  新数据对象1000  [x]数据对象2000[x]
            \         
             a
"""

验证>>> a = 1000; id(a)
2563691962960
>>> a = 2000; id(a)
2563691963024
>>> a = 1000; id(a)  # 第三次得到的id不同
2563691962992
# 证明1000被销毁,当然2000也被销毁了

在赋值操作a=10; a=20; a=10的过程中,10是否会被销毁?

不会,在第二次赋值时,仅改变了绑定联系。

10原本会随着第二次赋值而销毁,回收。但10是小整数对象池中的对象,本次程序执行小整数对象池数值将 “永久”、“单份” 保留于系统中,只有范围 [-5,256] 之外的数值对象才会遵循销毁规则——当引用计数为零。

a = 10
a = 20  # 对象10是否被销毁?
a = 10  # 对象20是否被销毁?

b = 10
c = 10

"""赋值绑定过程:
a = 10
...  数据对象10  ...
             \
              a
a = 20
...  数据对象10  数据对象20  ...
                /
              a
a = 10
...  数据对象10  数据对象20  ...
             \         
              a
b = 10
...  数据对象10  数据对象20  ...
         |   \         
         b    a
c = 10
...  数据对象10  数据对象20  ...
      /   |  \         
     c    b   a
"""
验证>>> a = 10; id(a)
140705097687728
>>> a = 20; id(a)
140705097688048
>>> a = 10; id(a)  # id相同,说明10对象“永久”存在
140705097687728

>>> b = 10; id(b)  # id相同,说明10对象“单份”存在
140705097687728
>>> c = 10; id(c)  # id相同,说明10对象“单份”存在
140705097687728