一、Python基础语法

1.注释的使用

单行注释

# 开头,在代码的上方说明, 示例代码如下:

(1) 以 # 开头,在代码的上方说明, 示例代码如下:

# 这是第一个单行注释
print("hello python")

为了保证代码的可读性,# 后面建议先添加一个空格,然后再编写相应的说明文字

(2) 以 # 开头,在代码的右侧(旁边)说明, 示例代码如下:

print("hello python")  # 输出 `hello python`

为了保证代码的可读性,注释和代码之间 至少要有 两个空格

多行注释

要在 Python 程序中使用多行注释,可以用 一对 连续的 三个 引号(单引号和双引号都可以)
示例代码如下:

"""
这是一个多行注释

在多行注释之间,可以写很多很多的内容……
""" 
print("hello python")

2. 变量的使用

变量定义

在 Python 中,每个变量 在使用前都必须赋值,变量 赋值以后 该变量 才会被创建
等号=用来给变量赋值,= 左边是一个变量名, = 右边是存储在变量中的值, 示例代码如下:

# 定义 qq 号码变量
qq_number = "1234567"

# 定义 qq 密码变量
qq_password = "123"

# 在程序中,如果要输出变量的内容,需要使用 print 函数
print(qq_number)
print(qq_password)

此外,还可以用 其他变量的计算结果 来定义变量, 变量定义之后,后续就可以直接使用了, 例如:

# 定义价格变量
price = 8.5

# 定义购买重量
weight = 7.5

# 计算金额
money = price * weight

print(money)

变量名 只有在 第一次出现 才是 定义变量,变量名 再次出现,是直接使用之前定义过的变量, 变量是可以重写赋值的.

money = money - 5

变量的类型

在内存中创建一个变量,会包括:

  • 变量的名称
  • 变量保存的数据
  • 变量存储数据的类型
  • 变量的地址
    在 Python 中定义变量是 不需要指定类型, 数据类型可以分为 数字型 和 非数字型
  • 数字型
    • 整型 (int)
    • 浮点型(float)
    • 布尔型(bool)
      • 真 True 非 0 数 —— 非零即真
      • 假 False 0
    • 复数型 (complex)
      • 主要用于科学计算
  • 非数字型
    • 字符串
    • 列表
    • 元组
    • 字典
    • set(集合)
      提示:在 Python 2.x 中,整数 根据保存数值的长度还分为:int(整数)long(长整数), 使用 type 函数可以查看一个变量的类型
      在这里插入图片描述

不同类型变量之间的计算

(1) 数字型变量 之间可以直接计算
在 Python 中,两个数字型变量是可以直接进行 算数运算的, 如果变量是 bool 型,在计算时True 对应的数字是 1,False 对应的数字是 0

(2) 字符串变量 之间使用 + 拼接字符串
在 Python 中,字符串之间可以使用 + 拼接生成新的字符串

first_name = "张"
last_name = "三"
print(first_name + last_name)

(3) 字符串变量 可以和 整数 使用 * 重复拼接相同的字符串
在这里插入图片描述

(4) 数字型变量 和 字符串 之间 不能进行其他计算
在这里插入图片描述

变量的输入

所谓 输入,就是 用代码 获取 用户通过 键盘 输入的信息, 在 Python 中,如果要获取用户在 键盘 上的输入信息,需要使用到 input 函数, 键盘输入的任何内容 Python 都认为是一个 字符串
在这里插入图片描述

类型转换

有时候,我们需要对数据内置的类型进行转换,数据类型的转换,一般情况下你只需要将数据类型作为函数名即可。
在这里插入图片描述
(1) 隐式类型转换
在隐式类型转换中,Python 会自动将一种数据类型转换为另一种数据类型,不需要我们去干预。
对两种不同类型的数据进行运算,较低数据类型(整数)就会转换为较高数据类型(浮点数)以避免数据丢失。如下所示:

num_int = 123
num_flo = 1.23
num_new = num_int + num_flo

print("datatype of num_int:", type(num_int))
print("datatype of num_flo:", type(num_flo))
print("datatype of num_new:", type(num_new))
print("Value of num_new:", num_new)

输出的结果:

datatype of num_int: <class 'int'>
datatype of num_flo: <class 'float'>
datatype of num_new: <class 'float'>
Value of num_new: 124.23

可以看到int和float运算后的类型变成了float

变量的格式化输出

在 Python 中可以使用 print 函数将信息输出到控制台,如果希望输出文字信息的同时,一起输出 数据,就需要使用到 格式化操作符,% 被称为 格式化操作符,专门用于处理字符串中的格式,包含 % 的字符串,被称为 格式化字符串,% 和不同的 字符 连用,不同类型的数据 需要使用 不同的格式化字符
在这里插入图片描述
语法格式如下:

print("格式化字符串" % 变量1)

print("格式化字符串" % (变量1, 变量2...))

例如:

name = "张三"
student_no = 110
price = 4.5
weight = 3
money = price * weight
scale = 0.1
print("我的名字叫 %s,请多多关照!" % name)
print("我的学号是 %06d" % student_no)
print("苹果单价 %.02f 元/斤,购买 %.02f 斤,需要支付 %.02f 元" % (price, weight, money))
print("数据比例是 %.02f%%" % (scale * 100))

输出结果:

我的名字叫 张三,请多多关照!
我的学号是 000110
苹果单价 4.50 元/斤,购买 3.00 斤,需要支付 13.50 元
数据比例是 10.00%

变量的命名

(1) 标识符
标示符就是程序员定义的 变量名、函数名
标示符可以由 字母、下划线 和 数字 组成, 不能以数字开头,不能与关键字重名

(2) 关键字
关键字 就是在 Python 内部已经使用的标识符, 它具有特殊的功能和含义,开发者 不允许定义和关键字相同的名字的标示符,通过以下命令可以查看 Python 中的关键字
通过以下代码可以查看内置的关键字

import keyword
print(keyword.kwlist)

输出结果:

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

命名规则 可以被视为一种 惯例,并无绝对与强制 目的是为了 增加代码的识别和可读性,Python 中的 标识符 是 区分大小写的,在定义变量时,为了保证代码格式,= 的左右应该各保留一个空格,在 Python 中,如果 变量名 需要由 二个 或 多个单词 组成时,可以按照以下方式命名:

  • 每个单词都使用小写字母
  • 单词与单词之间使用 _下划线 连接

3. 判断语句

if 语句使用

在 Python 中,if 语句 就是用来进行判断的,格式如下:

if 要判断的条件:
    条件成立时,要做的事情
    ……

例如:

# 1. 定义年龄变量
age = 18
# 2. 判断是否满 18 岁
# if 语句以及缩进部分的代码是一个完整的代码块
if age >= 18:
    print("可以进网吧嗨皮……")

else语句使用

else语句可以在不满足条件的时执行,格式如下:

if 要判断的条件:
    条件成立时,要做的事情
    ……
else:
    条件不成立时,要做的事情
    ……

注意:if 和 else 语句以及各自的缩进部分共同是一个 完整的代码块

elif语句使用

如果希望增加一些条件,条件不同,需要执行的代码也不同 时,就可以使用 elif,语法格式如下:

if 条件1:
    条件1满足执行的代码
    ……
elif 条件2:
    条件2满足时,执行的代码
    ……
elif 条件3:
    条件3满足时,执行的代码
    ……
else:
    以上条件都不满足时,执行的代码
    ……

注意:elif 和 else 都必须和 if 联合使用,而不能单独使用,可以将 if、elif 和 else 以及各自缩进的代码,看成一个 完整的代码块

if语句的嵌套使用

if 条件 1:
    条件 1 满足执行的代码
    ……

    if 条件 1 基础上的条件 2:
        条件 2 满足时,执行的代码
        ……    

    # 条件 2 不满足的处理
    else:
        条件 2 不满足时,执行的代码

# 条件 1 不满足的处理
else:
    条件1 不满足时,执行的代码
    ……

4. 运算符

算数运算符

在这里插入图片描述
在 Python 中 * 运算符还可以用于字符串,计算结果就是字符串重复指定次数的结果

比较运算符

在这里插入图片描述
另外,Python 2.x 中判断 不等于 还可以使用 <> 运算符

逻辑运算符

Python 中的 逻辑运算符 包括:与 (and)/或 (or)/非 (not) 三种
在这里插入图片描述

赋值运算符

在 Python 中,使用 = 可以给变量赋值
在这里插入图片描述

运算符的优先级

和数学中的运算符的优先级一致,在 Python 中进行数学计算时,同样也是:先乘除后加减, 同级运算符是 从左至右 计算, 可以使用 () 调整计算的优先级
以下表格的算数优先级由高到最低顺序排列
在这里插入图片描述

5. 循环语句

while 循环基本使用

循环的作用就是让 指定的代码 重复的执行,while 循环最常用的应用场景就是 让执行的代码 按照 指定的次数 重复 执行, 基本语法如下:

while 判断条件 :
    # 条件成立,重复执行

例如计算 0 ~ 100 之间所有偶数的累计求和结果

result = 0
i = 0
while i < 100:
    if i % 2 == 0:
        result += i
    i += 1
print("偶数结果是%d" % result)

注意:循环结束后,之前定义的计数器条件的数值是依旧存在的

break 和 continue关键字

break 和 continue 是专门在循环中使用的关键字,break 某一条件满足时,退出循环,不再执行后续重复的代码,continue 某一条件满足时,跳过后面的代码, 重新进入下一次循环
(1) break
在循环过程中,如果 某一个条件满足后,不再希望 循环继续执行,可以使用 break 退出循环, 例如:

i = 0
while i < 10:
    # break 某一条件满足时,退出循环,不再执行后续重复的代码
    if i == 3:
        break
    print(i)
    i += 1
print("over")

输出结果:

0
1
2
over

(2) continue
在循环过程中,某一条件满足时,跳过后面的代码, 重新进入下一次循环,可以使用 continue ,例如:

i = 0
while i < 5:
    if i == 3:
        i += 1
        continue  # 循环执行到此会重新进入循环
    # 重复执行的代码
    print(i)
    i += 1

输出结果:

0
1
2
4

while 循环嵌套

while 嵌套就是:while 里面还有 while

while 条件 1:
    条件1满足时,做的事情
    ...(省略)...

    while 条件 2:
        条件2满足时,做的事情
        ...(省略)...
        处理条件 2

    处理条件 1

使用示例
(1) 在控制台连续输出五行 *,每一行星号的数量依次递增

row = 0
while row < 5:
    row += 1
    print("*" * row)

输出结果:

*
**
***
****
*****

假设 Python 没有提供 字符串的 * 操作 拼接字符串,那么就需要使用嵌套循环来解决了, 代码如下:

row = 0
while row < 5:
    row += 1
    col = 0
    while col < row:
        print("*", end="")  # 默认end="\n",表示在结尾处输出一个换行符,这里替换为"",表示不换行
        col += 1
    print("")

(2) 在控制台中输出九九乘法表

# 定义起始行
row = 0
while row < 9:
    row += 1
    # 定义起始列
    col = 1
    while col <= row:
        # end="\t" 表示每一行后面带一个制表符
        print("%d * %d = %d" % (col, row, row * col), end="\t")
        col += 1
    print("")

结果如下:
在这里插入图片描述

注意 常用的的转义字符有
\ : 反斜杠符号
’ : 单引号
" :双引号
\n :换行
\t : 横向制表符
\r : 回车

while…else循环

语法如下:

while 布尔表达式:
    代码块
else:
    代码块

当 while 循环正常执行完的情况下,执行 else 输出,如果 while 循环中执行了跳出循环的语句,比如 break ,将不执 行 else 代码块的内容。
例如:

i=0
while i<5:
    print(i)
    i+=1
else:
    print('hello world')

输出结果:

0
1
2
3
4
hello world

for in 循环

语法如下:

for iterating_var in sequence:
   代码块

例如:

# 第一个实例
for letter in 'Python':     
   print '当前字母 :', letter

# 第二个实例
fruits = ['banana', 'apple',  'mango']
for fruit in fruits:        
   print '当前字母 :', fruit

# 第三个实例
for index in range(len(fruits)):
   print ('当前水果 : %s' % fruits[index])

输出结果:

当前字母 : P
当前字母 : y
当前字母 : t
当前字母 : h
当前字母 : o
当前字母 : n
当前字母 : banana
当前字母 : apple
当前字母 : mango
当前水果 : banana
当前水果 : apple
当前水果 : mango

for…else循环

for … else 表示的意思是else 中的语句会在循环正常执行完(即 for 不是通过 break 跳出而中断的)的情况下执行,和while else是同样的功能,例如:

for num in range(1, 5):
    print(num)
else:
    print(f"最后一个数是{num}")

输出结果:

1
2
3
4
最后一个数是4

6. 非数字型变量的使用

前面介绍过Python 中数据类型可以分为 数字型非数字型, 在 Python 中,所有 非数字型变量 都支持以下特点:

  • 都是一个 序列 sequence,也可以理解为 容器
  • 取值 []
  • 遍历 for in
  • 计算长度、最大/最小值、比较、删除
  • 链接 + 和 重复 *
  • 切片

列表

List(列表) 是 Python 中使用 最频繁 的数据类型,列表专门用于存储 一串 信息,列表用 [] 定义,数据 之间使用 , 分隔, 列表的 索引(下标) 从 0 开始

注意:从列表中取值时,如果 超出索引范围,程序会报错

列表常用操作

定义一个列表格式如下:

变量名 = []

常用的方法如下:
在这里插入图片描述
从上表中可以看出除了 删除指定索引的数据 和 获取列表长度 的使用较特殊, 其他都是通过列表名.函数名的方式使用. 例如:

name_list = ["zhangsan", "lisi", "wangwu"]
# 添加指定位置的新元素
name_list.insert(0, "tom")
# 末尾追加新元素
name_list.append("jack")
# 修改元素
name_list[1] = "李四"

print(name_list)

# 删除指定位置的元素
del name_list[2]
# 获取列表的长度
count = len(name_list)

print(name_list, count)

输出结果:

['tom', '李四', 'lisi', 'wangwu', 'jack']
['tom', '李四', 'wangwu', 'jack'] 4

列表的遍历

遍历 就是 从头到尾 依次 从 列表 中获取数据,在 Python 中为了提高列表的遍历效率,专门提供的 迭代 iteration 遍历,使用 for 就能够实现迭代遍历, 例如:

name_list = ["zhangsan", "lisi", "wangwu"]
for name in name_list:
    print("name=%s" % name)

输出结果:

name=zhangsan
name=lisi
name=wangwu

注意: Python 的 列表 中可以 存储不同类型的数据, 大多数情况下建议列表存储相同类型的数据

元组

元组的定义

Tuple(元组)/'tʌpl/ 与列表类似,不同之处在于元组的 元素不能修改, 元组表示多个元素组成的序列,用于存储 一串 信息,数据 之间使用 , 分隔,元组用 () 定义,元组的 索引 从 0 开始

创建空元组

info_tuple = ()
# 或者
info_tuple = tuple()

元组中 只包含一个元素 时,需要 在元素后面添加逗号

info_tuple = (50, )

元组常用操作

在pycharm中通过元组的名字输入.的时候会看到2个函数, count和index, 使用如下:

info = ("aaa", "aaa", "bbb", "ccc")
print(info.count("aaa"))
print(info.index("ccc"))

输出结果:

2
3

由此可知, count是计算元素在元组中出现的次数, index是获取元素在元组中的索引(下标),从0开始计算。

此外,通过pycharm的提示,发现可以使用__getitem__(索引)方法来获取指定位置的元组元素,例如:

info = ("aaa", "aaa", "bbb", "ccc")
print("第一个元素:%s,最后一个元素:%s" % 
    (info.__getitem__(0), info.__getitem__(len(info) - 1)))

元组的遍历

遍历 就是 从头到尾 依次 从 元组 中获取数据 ,同样可以用for in来循环, 使用如下:

for item in info:
    print(item)
  • 在 Python 中,可以使用 for 循环遍历所有 非数字型类型 的变量:列表、元组、字典 以及 字符串
  • 在实际开发中,除非 能够确认元组中的数据类型,否则针对元组的循环遍历需求并不是很多

元组和列表之间的转换

使用 list 函数可以把元组转换成列表

list(元组) 

使用 tuple 函数可以把列表转换成元组

tuple(列表)

例如:

# 定义元组info
info = ("aaa", "aaa", "bbb", "ccc")
print("info的内容:", info)
print("info的类型:", type(info))

# 元组转list
info_list = list(info)
print("info_list的内容:", info_list)
print("info_list的类型:", type(info_list))

# list转元组
info_tuple = tuple(info_list)
print("info_tuple的内容:", info_tuple)
print("info_tuple:", type(info_tuple))

输出结果:

info的内容: ('aaa', 'aaa', 'bbb', 'ccc')
info的类型: <class 'tuple'>

info_list的内容: ['aaa', 'aaa', 'bbb', 'ccc']
info_list的类型: <class 'list'>

info_tuple的内容: ('aaa', 'aaa', 'bbb', 'ccc')
info_tuple: <class 'tuple'>

元组和拆包

在 Python 中,将元组中的元素分配到多个变量中的过程被称为“元组拆包” , 可以使用逗号分隔变量列表,将元组中的元素按顺序分配给这些变量,例如:

a, b, c = (1, 2, 3)

在这个例子中,元组 (1, 2, 3) 中的元素按顺序分配给变量 a、b、c。
还可以使用星号运算符 * 来处理元组中的剩余元素。例如,可以使用以下方式来获得元组前两个元素和剩余元素:

a, b, *c = (1, 2, 3, 4, 5)

在这个例子中,变量 a 和 b 分别获得元组中的前两个元素。剩余的元素被分配给列表 c。在这种情况下,星号运算符 * 表示“收集”,用于收集元组中的剩余元素,并将它们分配给变量 c。

字典

字典的定义

dictionary(字典) 是 除列表以外 Python 之中 最灵活 的数据类型, 字典同样可以用来存储任意类型对象,通常用于存储 描述一个 物体 的相关信息

和列表的区别:

  • 列表 是 有序 的对象集合
  • 字典 是 无序 的对象集合

字典用 {} 定义,字典使用 键值对 存储数据,键值对之间使用 , 分隔

  • 键 key 是索引
  • 值 value 是数据
  • 键 和 值 之间使用 : 分隔
  • 键 必须是唯一的
  • 值 可以取任何数据类型,但 键 只能使用 字符串、数字或 元组

例如:

xiaoming = {"name": "小明",
            "age": 18,
            "gender": True,
            "height": 1.75}

字典常用操作

  • clear()

作用:删除字典中的所有项
返回值:该方法没有返回值,或者说返回None
用法:d.clear()

  • get()

作用:get方法也能够根据键获得值,但比d[key]的方式取值更好用,d[key]只适用于键存在的情况(否则会抛出异常),而get方法在键不存在时返回None,不会抛出异常
返回值:返回字典的key对应的value, 没有该value则返回None
用法:value = d.get(key) 或者 value = d.get(key, defaultValue),其中defaultValue在get取不到值的时候返回

  • items()

作用:返回一个包含所有字典项的列表,其中每个元素都为(key, value)的形式。
返回值:items方法返回的是一个"字典视图",dict_items类型, 里面是一个包含所有字典项的列表。
用法:t = d.items()
注意: "字典视图"是特殊类型,可用于迭代,可使用len(t)。但是不能当做正常的字典来用,比如:不能t[‘name’] 或者 t[0]

  • keys()

作用:返回一个包含所有字典key的列表,也属于"字典视图",类型为dict_keys。
用法:k = d.keys()

得到"字典视图"类型后可以转成list类型或者tuple类型,例如:

a = {'name': 'tom', 'age': 20}

a_key = a.keys()
print('type=%s,content=%s' % (type(a_key), a_key)) 
a_list = list(a_key)
print('type=%s,content=%s' % (type(a_list), a_list)) 
a_tuple = tuple(a_key)
print('type=%s,content=%s' % (type(a_tuple), a_tuple))

输出结果:

type=<class 'dict_keys'>,content=dict_keys(['name', 'age'])
type=<class 'list'>,content=['name', 'age']
type=<class 'tuple'>,content=('name', 'age')
  • values()

作用:返回一个包含所有字典value的列表,也属于"字典视图",类型为dict_values。
用法: v = d.values()
同理,得到"字典视图"类型后可以转成list类型或者tuple类型

  • pop(key , [default])

作用:用于获取与指定键相关联的值,并将该键-值对从字典中删除。需要注意的是,当指定的键不存在时,会抛异常。
用法:value = d.pop(key) 或者 value = d.pop(key,defaultValue),其中defaultValue在key不存在的情况下返回,否则会抛出异常

  • popitem()

作用:类似于list.pop(),但list.pop()弹出列表中的最后一个元素,而popitem()弹出的是第一个元素项, 类型是元组类型。但由于字典是无序的。最好使用OrderedDict来使用此方法。
返回值:弹出的字典项的元组形式。
用法:tur = d.popitem()

  • setdefault()

作用:方法setdefault有点像get,因为它也获取与指定键相关联的值,此外,setdefault 还会在字典不包含指定的键时,向字典中添加指定的键-值对, 值=None。
用法:value = d.setdefault(key)

  • update()

作用:使用一个字典中的项来更新另一个字典。原字典中已经存在该项时,就替换;不存在就创建。
用法:d.update(d1)

下面给出使用示例:

# 定义字典
xiao_ming = {
    "name": "小明",
    "age": 18,
    "gender": True,
    "height": 1.75
}

(1)get取值的操作

# 输出字典的类型
print("字典的类型: ", type(xiao_ming)) # 字典的类型:  <class 'dict'>
print(xiao_ming.get('name2') is None)  # True
print(xiao_ming.get('name2', "hello") is None)  # False

(2)item的取值

 items = xiao_ming.items()
 print("字典items的类型: ", type(items))
 print("字典items的内容: ", items)
 # items能够转list说明items是元组类型,只是元组的元素只有一个list列表
 print("字典items转list后的内容: ", list(items))

 for item in items:
     print("遍历字典items的元素类型:", type(item), end=" --> ")
     print("遍历元素的key=%s,value=%s", (item.__getitem__(0), item.__getitem__(1)))

输出结果:

字典items的类型:  <class 'dict_items'>
字典items的内容:  dict_items([('name', '小明'), ('age', 18), ('gender', True), ('height', 1.75)])
字典items转list后的内容:  [('name', '小明'), ('age', 18), ('gender', True), ('height', 1.75)]
遍历字典items的元素类型: <class 'tuple'> --> 遍历元素的key=%s,value=%s ('name', '小明')
遍历字典items的元素类型: <class 'tuple'> --> 遍历元素的key=%s,value=%s ('age', 18)
遍历字典items的元素类型: <class 'tuple'> --> 遍历元素的key=%s,value=%s ('gender', True)
遍历字典items的元素类型: <class 'tuple'> --> 遍历元素的key=%s,value=%s ('height', 1.75)

由此可以见,字典里面的每一个item都是元组类型

(3)keys取值

keys = xiao_ming.keys()
print("字典keys的类型: ", type(keys))
print("字典keys的内容: ", keys)
for key in keys:
    print("key=%s,类型=%s" % (key, type(key)))

输出结果:

字典keys的类型:  <class 'dict_keys'>
字典keys的内容:  dict_keys(['name', 'age', 'gender', 'height'])
key=name,类型=<class 'str'>
key=age,类型=<class 'str'>
key=gender,类型=<class 'str'>
key=height,类型=<class 'str'>

(4)values取值

values = xiao_ming.values()
print("字典values的类型: ", type(values))
print("字典values的内容: ", values)
for value in values:
    print("遍历value=%s,类型=%s" % (value, type(value)))

输出结果:

字典values的类型:  <class 'dict_values'>
字典values的内容:  dict_values(['小明', 18, True, 1.75])
遍历value=小明,类型=<class 'str'>
遍历value=18,类型=<class 'int'>
遍历value=True,类型=<class 'bool'>
遍历value=1.75,类型=<class 'float'>

(5)pop取值

# 取值后 'name':'小明' 这个键值对就会移除
element = xiao_ming.pop("name")
print("element的类型: ", type(element))
print("element的内容: ", element)

# 添加默认值,否则会找不到key会抛出KeyError: 'name2'异常
element2 = xiao_ming.pop("name2", "李四")
print("element2的类型: ", type(element2))
print("element2的内容: ", element2)

# 取值后的字典内容,name=小明 已不再
print("pop后的字典内容:", xiao_ming)

输出结果:

element的类型:  <class 'str'>
element的内容:  小明
element2的类型:  <class 'str'>
element2的内容:  李四
pop后的字典内容: {'age': 18, 'gender': True, 'height': 1.75}

(6)popitem取值

item = xiao_ming.popitem()
print("item的类型:", type(item))
print("item的内容:", item)

输出结果:

item的类型: <class 'tuple'>
item的内容: ('height', 1.75)

可以看到popItem会取出一个元组。

(7)setdefault取值

default = xiao_ming.setdefault("age")
print("default的类型:", type(default))
print("default的内容:", default)

# setdefault如果取值不到不会报错,而是返回None
default2 = xiao_ming.setdefault("sex")
print("default2的类型:", type(default2))
print("default2的内容:", default2)

# 也可以在取值不到的时候设置默认值
default3 = xiao_ming.setdefault("sex", 1)
print("default3的类型:", type(default3))
print("default3的内容:", default3)
print("操作后的字典内容:", xiao_ming)

输出结果:

default的类型: <class 'int'>
default的内容: 18
default2的类型: <class 'NoneType'>
default2的内容: None
default3的类型: <class 'NoneType'>
default3的内容: None
操作后的字典内容: {'name': '小明', 'age': 18, 'gender': True, 'height': 1.75, 'sex': None}

(8)update更新操作

 xiao_ming.update({'phone': 119})  # 此时字典中多了一个键值对 phone:119
print("添加phone后:", xiao_ming)
xiao_ming.update({'age': 14})  # 此时age的值会被替换为14
print("修改age后:", xiao_ming)

输出结果:

{'name': '小明', 'age': 18, 'gender': True, 'height': 1.75, 'phone': 119}
{'name': '小明', 'age': 14, 'gender': True, 'height': 1.75, 'phone': 119}

关于字典无序的说明:
Python字典中的项无序在python2.x版本中能够容易的体现到。在python3.x中,Python字典会按照其中项的创建顺序为它们排位,即它们有初始顺序。但我们无法对这初始排序做些什么。下面的实例能够体现Python字典的无序性:
d1 = {‘name’: ‘David’, ‘age’: 25}
d2 = {‘age’: 25, ‘name’: ‘David’}
print(d1 == d2) # True
d1 = OrderedDict([(‘name’, ‘David’), (‘age’, 25)])
d2 = OrderedDict([(‘age’, 25), (‘name’, ‘David’)])
print(d1 == d2) # False

字典的遍历

同样可以使用for in来遍历, 遍历得到的是字典的key, 用法如下:

xiao_ming = {
    "name": "小明",
    "age": 18,
    "gender": True,
    "height": 1.75
}

for k in xiao_ming:
    print("key=%s,value=%s" % (k, xiao_ming[k]))

输出结果:

key=name,value=小明
key=age,value=18
key=gender,value=True
key=height,value=1.75

set集合

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

set的创建

使用花括号用来创建set类型的变量,这与字典很类似,只是缺少了value,set类型和字典的key有一些类似之处,比如:无序、不可重复、必须是hashable的,所以用花括号表示,是理所当然的。

哈希 是一种 算法,其作用就是提取数据的 特征码(指纹)

  • 相同的内容 得到 相同的结果
  • 不同的内容 得到 不同的结果

常用方法

  • add(key)
    通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果

  • remove(key)
    通过remove(key)方法可以删除元素

  • 交集、并集操作
    set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作

  • pop()
    取出首个元素

用法示例:

# 定义set
my_set = {"a", 123, True}
print(my_set)
# 添加元素,重复的元素会去重
my_set.add(123)
my_set.add("abc")
# update操作
my_set.update({"hello world", "hi python"})
print(my_set)
# pop操作, 取出第一个元素
my_set.pop()
print(my_set)
# 交集和并集处理
my_set1 = {"a", 123, True}
my_set2 = {"a", 123, "abc"}
print("交集:", my_set1 & my_set2)
print("并集:", my_set1 | my_set2)

输出结果:

{True, 123, 'a'}
{True, 'hi python', 'a', 123, 'abc', 'hello world'}
{'hi python', 'a', 123, 'abc', 'hello world'}
交集: {123, 'a'}
并集: {True, 'a', 123, 'abc'}

字典的拆包

Python 中将字典中的键值对拆包到变量中的过程被称为“字典解包”(dictionary unpacking)。
可以使用两个星号运算符 ** 将字典中的所有键值对解包到一个字典中,并将其赋值给一个变量,例如:

my_dict = {'a': 1, 'b': 2, 'c': 3}
new_dict = {**my_dict}

也可以将字典中的所有键值对中的值按顺序分配给变量 a、b、c。

a, b, c = my_dict

同样也支持使用星号运算符将剩余的值分配到一个列表中, 例如:

my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
a, b, *c = my_dict

字符串

字符串的定义

字符串 就是 一串字符,是编程语言中表示文本的数据类型,在 Python 中可以使用 一对双引号 " 或者 一对单引号 ’ 定义一个字符串.

虽然可以使用 " 或者 ’ 做字符串的转义,但是在实际开发中:

  • 如果字符串内部需要使用 ",可以使用 ’ 定义字符串
  • 如果字符串内部需要使用 ',可以使用 " 定义字符

可以使用 索引 获取一个字符串中 指定位置的字符,索引计数从 0 开,也可以使用 for 循环遍历 字符串中每一个字符

使用示例:

string = "Hello Python"

for c in string:
    print(c)

字符串的常用操作

在 pycharm 中定义一个 字符串,例如:hello_str = “”, 输入 hello_str. 此时,pycharm 会提示 字符串 能够使用的 方法 如下:

hello_str.capitalize    hello_str.isidentifier  hello_str.rindex
hello_str.casefold      hello_str.islower       hello_str.rjust
hello_str.center        hello_str.isnumeric     hello_str.rpartition
hello_str.count         hello_str.isprintable   hello_str.rsplit
hello_str.encode        hello_str.isspace       hello_str.rstrip
hello_str.endswith      hello_str.istitle       hello_str.split
hello_str.expandtabs    hello_str.isupper       hello_str.splitlines
hello_str.find          hello_str.join          hello_str.startswith
hello_str.format        hello_str.ljust         hello_str.strip
hello_str.format_map    hello_str.lower         hello_str.swapcase
hello_str.index         hello_str.lstrip        hello_str.title
hello_str.isalnum       hello_str.maketrans     hello_str.translate
hello_str.isalpha       hello_str.partition     hello_str.upper
hello_str.isdecimal     hello_str.replace       hello_str.zfill
hello_str.isdigit       hello_str.rfind

下面针对这些方法做分类介绍

(1)判断类型
在这里插入图片描述

用法示例:

print("   .isspace():", "   ".isspace())
print("abc123.isalnum():", "abc123".isalnum())
print("abc.isalpha():", "abc".isalpha())
print("123123.isdecimal():", "123123".isdecimal())
print("123123.isdigit():", "123123".isdigit())
print("123123一二三.isnumeric():", "123123一二三".isnumeric())
print("Hello Work.istitle():", "Hello Work".istitle())
print("hello.islower():", "hello".islower())
print("hello.isupper():", "hello".isupper())

输出结果:

   .isspace(): True
abc123.isalnum(): True
abc.isalpha(): True
123123.isdecimal(): True
123123.isdigit(): True
123123一二三.isnumeric(): True
Hello Work.istitle(): True
hello.islower(): True
hello.isupper(): False

(2)查找和替换
在这里插入图片描述

用法示例:

hello_str = "hello work"
print("startswith:", hello_str.startswith("or"))
print("endswith:", hello_str.endswith("rk"))
print("find(or):", hello_str.find("or"))
print("find(or,0,5):", hello_str.find("or", 0, 5))
print("rfind(or):", hello_str.rfind("or"))
print("rfind(or,0,5):", hello_str.rfind("or", 0, 5))
print("index(or):", hello_str.index("or"))
print("index(or,0,len):", hello_str.index("or", 0, len(hello_str)))
print("rindex(or):", hello_str.rindex("or"))
print("rindex(or,0,len):", hello_str.rindex("or", 0, len(hello_str)))
print("replace(or,oo):", hello_str.replace("or", "oo"))
print("replace(o,p,count):", hello_str.replace("o", "p", hello_str.count("o")))

输出结果:

startswith: False
endswith: True
find(or): 7
find(or,0,5): -1
rfind(or): 7
rfind(or,0,5): -1
index(or): 7
index(or,0,len): 7
rindex(or): 7
rindex(or,0,len): 7
replace(or,oo): hello wook
replace(o,p,count): hellp wprk

(3)大小写转换
在这里插入图片描述
用法示例:

hello_str = "hEllo woRk"
print("capitalize:", hello_str.capitalize())
print("title:", hello_str.title())
print("lower:", hello_str.lower())
print("upper:", hello_str.upper())
print("swapcase:", hello_str.swapcase())

输出结果:

capitalize: Hello work
title: Hello Work
lower: hello work
upper: HELLO WORK
swapcase: HeLLO WOrK

(4)文本对齐
在这里插入图片描述
用法示例:

hello_str = "hello work"
print("(", hello_str.ljust(30), ")", sep="")
print("hello_str长度:", len(hello_str.ljust(30)))
print("(", hello_str.rjust(30), ")", sep="")
print("(", hello_str.center(30), ")", sep="")

输出结果:

(hello work                    )
hello_str长度: 30
(                    hello work)
(          hello work          )

(5)去除空白字符
在这里插入图片描述
用法示例:

hello_str = "  hello work   "
print("hello_str.lstrip():", "(", hello_str.lstrip(), ")", sep="")
print("hello_str.rstrip():", "(", hello_str.rstrip(), ")", sep="")
print("hello_str.strip():", "(", hello_str.strip(), ")", sep="")

输出结果:

hello_str.lstrip():(hello work   )
hello_str.rstrip():(  hello work)
hello_str.strip():(hello work)

(6)拆分和连接
在这里插入图片描述
使用示例:

hello_str = "hello work\nabc"
# 返回元组
print("hello_str.partition(or):", hello_str.partition("or"))
print("hello_str.rpartition(or):", hello_str.rpartition("or"))
# 返回列表
print("hello_str.split(or):", hello_str.split("or"))
print("hello_str.split(o):", hello_str.split("o"))
print("hello_str.split(o, 1):", hello_str.split("o", 1))
print("hello_str.split(o, 2):", hello_str.split("o", 2))
print("hello_str.splitlines():", hello_str.splitlines())
# 返回拼接后的字符串
print(".join(hello_str):", ".".join(hello_str))

输出结果:

hello_str.partition(or): ('hello w', 'or', 'k\nabc')
hello_str.rpartition(or): ('hello w', 'or', 'k\nabc')
hello_str.split(or): ['hello w', 'k\nabc']
hello_str.split(o): ['hell', ' w', 'rk\nabc']
hello_str.split(o, 1): ['hell', ' work\nabc']
hello_str.split(o, 2): ['hell', ' w', 'rk\nabc']
hello_str.splitlines(): ['hello work', 'abc']
.join(hello_str): h.e.l.l.o. .w.o.r.k.
.a.b.c

切片操作

切片 方法适用于 字符串、列表、元组
使用格式如下:

字符串/列表/元组[开始索引:结束索引:步长]
  • 切片开始索引默认为0,结束索引默认为字符串/列表/元组的长度,步长默认为1。
  • 指定的区间属于 左闭右开 型 [开始索引, 结束索引),步长就是每几步取一个元素。
  • 切片的索引和步长也可以是负数
  • 最后一位的索引等于 字符串/列表/元组 的长度减1也可以是-1,倒数第二位的索引可以是 字符串/列表/元组 的长度减2,也可以是-2,步长为-1即代表从右向左每一位截取一个(反向截取)
  • 当步长为正数时,代表从左向右截取,那么开始索引不能大于结束索引,否则截取不到内容;当步长为负数时,代表从右向左截取,那么开始索引不能小于结束索引,否则截取不到内容;当然开始索引和结束索引相等也会得不到内容。

字符串切片

切片 使用 索引值 来限定范围,从一个大的 字符串 中 切出 小的 字符串
使用示例:

num_str = "0123456789"
print("截取从2~5位置的字符串[2:6]:", num_str[2:6])  # 2345
print("截取从2~5位置的字符串,每隔一个字符截取[2:6:2]:", num_str[2:6:2])  # 24
print("截取从2~`末尾` 的字符串[2:]:", num_str[2:])  # 23456789
print("截取从`开始`~5位置 的字符串[:6]:", num_str[:6])  # 012345
print("截取完整的字符串[:]:", num_str[:])  # 0123456789
print("截取0~`末尾`,步长为3:", num_str[0::3])  # 0369
print("从开始位置,每隔一个字符截取[::2]:", num_str[::2])  # 02468
print("从索引1开始,每隔一个取一个[1::2]:", num_str[1::2])  # 13579
print("截取最后一个字符[-1]:", num_str[-1])  # 9
print("截取第一个字符[0]:", num_str[0])  # 0
print("截取从2~`末尾 - 1`的字符串[2:-1]:", num_str[2:-1])  # 2345678
print("截取字符串末尾两个字符[-2:]:", num_str[-2:])  # 89
print("字符串的逆序截取[::-1]:", num_str[::-1])  # 9876543210
print("字符串的逆序截取,每隔一个字符截取[::-2]:", num_str[::-2])  # 97531

列表的切片操作

num_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print("截取从2~5位置的元素[2:6]:", num_list[2:6])  # [2, 3, 4, 5]
print("截取从2~5位置的元素,每隔一个元素截取[2:6:2]:", num_list[2:6:2])  # [2, 4]
print("截取从2~`末尾` 的元素[2:]:", num_list[2:])  # [2, 3, 4, 5, 6, 7, 8, 9]
print("截取从`开始`~5位置 的元素[:6]:", num_list[:6])  # [0, 1, 2, 3, 4, 5]
print("截取完整的元素[:]:", num_list[:])  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print("截取从0~`末尾`[0::3],步长为3:", num_list[0::3])  # [0, 3, 6, 9]
print("从开始位置,每隔一个元素截取[::2]:", num_list[::2])  # [0, 2, 4, 6, 8]
print("从索引1开始,每隔一个取一个[1::2]:", num_list[1::2])  # [1, 3, 5, 7, 9]
print("截取最后一个元素[-1]:", num_list[-1])  # 9
print("截取第一个元素[0]:", num_list[0])  # 0
print("截取从2~`末尾 - 1`的元素[2:-1]:", num_list[2:-1])  # [2, 3, 4, 5, 6, 7, 8]
print("截取元素末尾两个元素[-2:]:", num_list[-2:])  # [8, 9]
print("元素的逆序截取[::-1]:", num_list[::-1])  # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print("元素的逆序截取,每隔一个元素截取[::-2]:", num_list[::-2])  # [9, 7, 5, 3, 1]

运算符操作

在这里插入图片描述

注意: in 在对 字典 操作时,判断的是 字典的键,in 和 not in 被称为 成员运算符,成员运算符用于 检测 序列中是否包含指定的 成员

7. 变量的引用和作用域

变量的引用

变量 和 数据 都是保存在 内存 中的

  • 变量 和 数据 是分开存储的
  • 数据 保存在内存中的一个位置(地址)
  • 变量 中保存着数据在内存中的位置(地址)
  • 变量 中 记录数据的地址,就叫做 引用
  • 使用 id() 函数可以查看变量中保存数据所在的 内存地址

注意:如果变量已经被定义,当给一个变量赋值的时候,本质上是 修改了数据的引用

  • 变量 不再 对之前的数据引用
  • 变量 改为 对新赋值的数据引用
  • 实际上当我们对一个变量赋值的时候,我们的变量并没有存储这个值。是绑定了一个内存地址id,当我们要用这个变量的值的时候,就去内存中寻找这个地址的存储的值

看个示例:

def main():
    a = "hello,world"
    b = a
    c = [1, 2, 3]
    print("a指向的地址:", id(a))
    print("b指向的地址:", id(b))
    print("c指向的地址:", id(c))


if __name__ == '__main__':
    main()

输出结果:

a指向的地址: 4347806256
b指向的地址: 4347806256
c指向的地址: 4346438208

可以看到变量a和b引用的地址都是一样的. 这里用一张图来说明
在这里插入图片描述
我们再看看,改变a的变量会发生什么?

def main():
    a = "hello,world"
    print("改变前a指向的地址:", id(a))
    a = "123456"
    print("改变后a指向的地址:", id(a))

输出结果:

改变前a指向的地址: 4381426352
改变后a指向的地址: 4381426480

来看看图示:
在这里插入图片描述

注意: 没有再被任何引用持有的内存数据会被python的垃圾回收器自动回收

如果变量a和b赋值的内容都相同会发生什么呢?

def main():
    a = "hello,world"
    b = "hello,world"
    print("a指向的地址:", id(a))
    print("b指向的地址:", id(b))

输出结果:

a指向的地址: 4343497136
b指向的地址: 4343497136

此时a和b执行的地址都是一样的, 如下图所示:
在这里插入图片描述
由此可知, 相同内容的字符串并不会重复创建对象,而是复用之前的创建的对象。
同样, int、float、bool、复数、元组类型也适用。如下所示:

def main():
    a = 123
    b = 123
    print("数字a指向的地址:", id(a))
    print("数字b指向的地址:", id(b))
    
    a = True
    b = True
    print("布尔值a指向的地址:", id(a))
    print("布尔值b指向的地址:", id(b))
    
    a = 123.0
    b = 123.0
    print("浮点a指向的地址:", id(a))
    print("浮点b指向的地址:", id(b))
    
    a = (1, 2, 3)
    b = (1, 2, 3)
    print("元组a指向的地址:", id(a))
    print("元组b指向的地址:", id(b))

输出结果:

数字a指向的地址: 4335202376
数字b指向的地址: 4335202376
布尔值a指向的地址: 4334979928
布尔值b指向的地址: 4334979928
浮点a指向的地址: 4336861488
浮点b指向的地址: 4336861488
元组a指向的地址: 4337772672
元组b指向的地址: 4337772672

如果a、b变量赋值的是列表、字典、集合呢?

def main():
    a = [1, 2, 3]
    b = [1, 2, 3]
    print("列表a指向的地址:", id(a))
    print("列表b指向的地址:", id(b))
    
    a = {"name": "123"}
    b = {"name": "123"}
    print("字典a指向的地址:", id(a))
    print("字典b指向的地址:", id(b))

    a = {1, 2, 3}
    b = {1, 2, 3}
    print("集合a指向的地址:", id(a))
    print("集合b指向的地址:", id(b))

输出结果:

列表a指向的地址: 4310196736
列表b指向的地址: 4310196672
字典a指向的地址: 4310125632
字典b指向的地址: 4310125696
集合a指向的地址: 4310475488
集合b指向的地址: 4311178816

可见, 列表、字典、集合数据类型每次定义都会创建一个新的对象。

变量的可变性

python中的变量分为可变类型和不可变类型, 怎么理解这里的"变"呢? 所谓的"可变"是指变量指向的内存数据是可变。

  • 不可变类型,内存中的数据不允许被修改:
    • 数字类型 int, bool, float, complex, long(2.x)
    • 字符串 str
    • 元组 tuple
  • 可变类型,内存中的数据可以被修改:
    • 列表 list
    • 字典 dict
    • 集合 set

注意: 当不可变的变量尝试去改变内存数据时, 以字符串为例,原本的字符串对象并没有被修改,而是创建了新的字符串对象, 重新赋值给了该变量, 例如:

def main():
    a = "abc"
    print("修改前a指向的地址:", id(a))
    # 修改a的值为"aaa"
    a = "aaa"
    print("修改后a指向的地址:", id(a))

输出结果:

修改前a指向的地址: 4374930032
修改后a指向的地址: 4381197040

可见不可变类型的变量,当引用的值发生改变时,对象也随之创建了。

对于列表、字典、set这种不可变类型的变量, 无论如何修改数据内容,只要引用不发生改变, 那么就不会创建新的对象, 例如:

def main():
    a = [1, 2, 3]
    b = {1, 2, 3}
    c = {"name": "tom"}
    print("修改前a指向的地址:%s, 内容:%s" % (id(a), a))
    print("修改前b指向的地址:%s, 内容:%s" % (id(b), b))
    print("修改前c指向的地址:%s, 内容:%s" % (id(c), c))
    print("======================================")
    # 修改
    a[0] = 0
    b.update({2, 3, 4})
    c["name"] = "jack"

    print("修改后a指向的地址:%s, 内容:%s" % (id(a), a))
    print("修改后b指向的地址:%s, 内容:%s" % (id(b), b))
    print("修改后c指向的地址:%s, 内容:%s" % (id(c), c))

输出结果:

修改前a指向的地址:4448228800, 内容:[1, 2, 3]
修改前b指向的地址:4448133184, 内容:{1, 2, 3}
修改前c指向的地址:4447786304, 内容:{'name': 'tom'}
======================================
修改后a指向的地址:4448228800, 内容:[0, 2, 3]
修改后b指向的地址:4448133184, 内容:{1, 2, 3, 4}
修改后c指向的地址:4447786304, 内容:{'name': 'jack'}

注意:

  • 字典的 key 只能使用不可变类型的数据
  • 可变类型的数据变化,是通过 方法 操作来实现的
  • 如果给一个可变类型的变量,赋值了一个新的数据,引用会修改

变量的浅拷贝与深拷贝

Python中有一个copy模块, 通过copy模块的copy方法可以实现变量的浅拷贝, 通过deepcopy方法可以实现深拷贝。

  • 浅拷贝是创建一个新的对象(不可变变量除外),其中包含原始对象的引用,并复制原始对象中所包含的可变对象(如列表、字典等),但不会复制可变对象中的元素。换句话说,在新的对象中,对原始对象中的列表和字典进行的修改会反映在新对象中的相应列表和字典中。
  • 深拷贝是创建一个新的对象(不可变变量除外),并复制原始对象中所包含的所有可变对象及其元素。换句话说,它会递归地复制对象本身以及其中的所有子对象。因此,对新的对象或原始对象进行的修改不会相互影响。

(1) 对象是不可变数据类型, 子对象也是不可变数据类型

import copy

a = (1, 2)  # a是不可变变量元组, 子元素也是不可变变量int
b = copy.copy(a)  # 浅拷贝,不会创建新对象
c = copy.deepcopy(a)  # 深拷贝,不会创建新对象
# 查看变量内存地址
print(id(a))  # 4303470080
print(id(b))  # 4303470080
print(id(c))  # 4303470080

(2) 对象不可变数据类型, 子对象是可变数据类型

import copy

a1 = ([1, 2],)  # a1是不可变变量元组, 子元素是可变变量列表
b1 = copy.copy(a1)  # 浅拷贝,不会创建新对象
c1 = copy.deepcopy(a1)  # 深拷贝,会创建新对象
print(id(a1))  # 4338550384
print(id(b1))  # 4338550384
print(id(c1))  # 4338473856
# 修改原数据的内容
a1[0].append(3)
print(a1)  # ([1, 2, 3],)
print(b1)  # ([1, 2, 3],)
print(c1)  # ([1, 2],)

(3) 对象是可变数据类型, 子对象是不可变数据类型

import copy

a2 = [1, 2]  # a2 是可变变量列表, 子元素是不可变变量int
b2 = copy.copy(a2)  # 浅拷贝, 会创建新对象
c2 = copy.deepcopy(a2)  # 深拷贝, 会创建新对象
# 查看变量内存地址
print(id(a2))  # 4305351616
print(id(b2))  # 4305289728
print(id(c2))  # 4305351552
# 修改原数据的内容
a2.append(3)
print(a2)  # [1, 2, 3]
print(b2)  # [1, 2]
print(c2)  # [1, 2]

(4) 对象本身是可变数据类型,子对象也是可变数据类型

a3 = [[1], [2]]  # a3 是可变变量列表, 子元素是可变变量列表
b3 = copy.copy(a3)  # 浅拷贝, 会创建新对象
c3 = copy.deepcopy(a3)  # 深拷贝, 会创建新对象
# 查看变量内存地址
print(id(a3))  # 4302677248
print(id(b3))  # 4302713920
print(id(c3))  # 4302713856

# 修改原数据的内容
a3[0].append(2)
print(a3)  # [[1, 2], [2]]
print(b3)  # [[1, 2], [2]]
print(c3)  # [[1], [2]]

通过上面的例子可以看到, 对于浅拷贝和深拷贝是否会创建对象, 不仅要看对象本身是否是可变数据类型, 还得看包含的对象是否是可变数据类型。

  • 对象本身是不可变数据类型,子对象也是不可变数据类型
    • 浅拷贝和深拷贝都不会创建新对象, 拷贝的内容只是引用拷贝
  • 对象本身是不可变数据类型,子对象是可变数据类型
    • 浅拷贝不会创建对象,拷贝的内容只是引用拷贝
    • 深拷贝会创建对象, 拷贝的内容会完全创建一份
  • 对象本身是可变数据类型,子对象是不可变数据类型
    • 浅拷贝会创建对象, 拷贝的内容会完全创建一份
    • 深拷贝会创建对象, 拷贝的内容会完全创建一份
  • 对象本身是可变数据类型,子对象也是可变数据类型
    • 浅拷贝会创建对象, 拷贝的内容只是引用拷贝
    • 深拷贝会创建对象, 拷贝的内容会完全创建一份

注意:
列表的切片操作和字典的copy操作都是浅拷贝.他们都会创建新的对象, 对于列表来说, 列表内的子元素会进行引用拷贝; 相似的, 字典内的key对应的value也是引用拷贝, 指向的数据是原字典的key对应的value。
另外,函数的参数传递也是浅拷贝。

局部变量和全局变量

  • 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
  • 全局变量 是在 函数外部 定义的变量(没有定义在某一个函数内),所有函数内部都可以使用这个变量

局部变量

  • 函数执行结束后,函数内部的局部变量,会被系统回收
  • 不同的函数,可以定义相同的名字的局部变量,但是 彼此之间 不会产生影响

示例:

def demo1():
    num = 10
    print("demo1->", num)
    num = 20
    print("demo1->修改后 %d" % num)


def demo2():
    num = 100
    print("demo2->", num)


if __name__ == '__main__':
    demo1()
    demo2()

输出结果:

demo1-> 10
demo1->修改后 20
demo2-> 100

局部变量的生命周期

  • 所谓 生命周期 就是变量从 被创建 到 被系统回收 的过程
  • 局部变量 在 函数执行时 才会被创建
  • 函数执行结束后 局部变量 被系统回收
  • 局部变量在生命周期 内,可以用来存储 函数内部临时使用到的数据

全局变量

全局变量 是在 函数外部定义 的变量,所有函数内部都可以使用这个变量, 示例:

# 定义一个全局变量
num = 10

def demo1():
    print("demo1->", num)

def demo2():
    print("demo2->", num)

if __name__ == '__main__':
    demo1()
    demo2()

输出结果:

demo1-> 10
demo2-> 10

注意:函数执行时,变量的查找顺序如下:

  1. 首先 查找 函数内部 是否存在 指定名称 的局部变量,如果有,直接使用
  2. 如果没有,查找 函数外部 是否存在 指定名称 的全局变量,如果有,直接使用
  3. 如果还没有,程序报错!

虽然全局变量可以被任何函数内访问,但是却不能在函数内部修改它, 如果使用赋值语句修改全局变量是无效的,它只会在函数内创建一个同名的局部变量而已, 例如:

num = 10

def demo1():
    # 只是定义了一个局部变量,不会修改到全局变量,只是变量名相同而已
    num = 100
    print("demo1->", num)

def demo2():
    print("demo2->", num)

if __name__ == '__main__':
    demo1()
    demo2()

输出结果:

demo1-> 100
demo2-> 10

有没有什么办法可以在函数内部修改全局变量的值呢?
有的, 如果在函数中需要修改全局变量,需要使用 global 进行声明

num = 10

def demo1():
    # global 关键字,告诉 Python 解释器 num 是一个全局变量
    global num
    # 这时候修改的就是全局变量了
    num = 100
    print("demo1->", num)

def demo2():
    print("demo2->", num)

if __name__ == '__main__':
    demo1()
    demo2()

输出结果:

demo1-> 100
demo2-> 100

全局变量的位置

关于全局变量定义的位置,为了保证所有的函数都能够正确使用到全局变量,应该 将全局变量定义在其他函数的上方, 否则会报错例如:

a = 10


def demo():
    print("%d" % a)
    print("%d" % b)
    print("%d" % c)

b = 20
demo()
c = 30
10
20
Traceback (most recent call last):
  File "/Users/chenyousheng/workspace/python/demo1/main.py", line 12, in <module>
    demo()
  File "/Users/chenyousheng/workspace/python/demo1/main.py", line 9, in demo
    print("%d" % c)
NameError: name 'c' is not defined

可以看到只输出a和b的值,然后就报错了, 因为全局变量 c,是在调用函数demo之后才定义的, 因此无法访问c变量.

8. 函数

函数的作用,在开发程序时,使用函数可以提高编写的效率以及代码的重用

函数的定义

定义函数的格式如下:

def 函数名():

    函数封装的代码
    ……

def 是英文 define 的缩写, 函数名称应该能够表达函数封装代码的功能,方便后续的调用,
函数名称的命名应该 符合 标识符的命名规则: 可以由字母、下划线和数字组成,不能以数字开头,不能与关键字重名.

函数调用

调用函数很简单的,通过 函数名() 即可完成对函数的调用

name = "小明"


# 定义函数
def say_hello():
    print("say_hello")


print(name)
# 只有在调用函数时,之前定义的函数才会被执行
# 函数执行完成之后,会重新回到之前的程序中,继续执行后续的代码
say_hello()

print(name)

结果输出:

小明
say_hello
小明

注意: 调用函数之前,必须要保证 Python 已经知道函数的存在, 因此不能将 函数调用 放在 函数定义 的上方

函数参数的使用

函数的参数 可以增加函数的 通用性,针对 相同的数据处理逻辑,在函数 内部,把参数当做 变量 使用
函数的参数可以在函数括号内通过 , 分隔, 例如:

def sum_num(num1, num2):
    result = num1 + num2
    print("%d + %d = %d" % (num1, num2, result))


sum_num(50, 20)

形参和实参

  • 形参:定义 函数时,小括号中的参数,是用来接收参数用的,在函数内部 作为变量使用
  • 实参:调用 函数时,小括号中的参数,是用来把数据传递到 函数内部 用的

不可变与可变参数

  • 无论参数是可变还是不可变变量,只要在函数内部进行了赋值操作,就不会影响到外部变量引用,例如:
def demo(num, num_list):
    print("函数内部")

    # 赋值语句
    num = 200
    num_list = [1, 2, 3]

    print(num)
    print(num_list)

    print("函数代码完成")


if __name__ == '__main__':
    gl_num = 99
    gl_list = [4, 5, 6]
    demo(gl_num, gl_list)
    print(gl_num)
    print(gl_list)

输出结果:

函数内部
200
[1, 2, 3]
函数代码完成
99
[4, 5, 6]

  • 如果传递的参数是 可变类型,在函数内部,使用方法修改了数据的内容,同样会影响到外部的数据,例如:
def mutable(num_list):
    num_list.extend([1, 2, 3])

    print("函数内输出:", num_list)


if __name__ == '__main__':
    gl_list = [6, 7, 8]
    mutable(gl_list)
    print("函数外输出:", gl_list)

输出结果:

函数内输出: [6, 7, 8, 1, 2, 3]
函数外输出: [6, 7, 8, 1, 2, 3]

缺省参数

定义函数时,可以给 某个参数 指定一个默认值,具有默认值的参数就叫做 缺省参数, 当调用函数时,如果没有传入缺省参数的值,则在函数内部使用定义函数时指定的默认参数的值, 例如列表的sort方法就有一个缺省参数, 使用如下:

if __name__ == '__main__':
    gl_num_list = [6, 3, 9]

    # 默认就是升序排序,因为这种应用需求更多
    gl_num_list.sort()
    print(gl_num_list)

    # 只有当需要降序排序时,才需要传递 `reverse` 参数
    gl_num_list.sort(reverse=True)
    print(gl_num_list)

输出结果:

[3, 6, 9]
[9, 6, 3]

要自定义的话也很简单, 例如:

def print_info(name, gender=True):
    gender_text = "男生"
    if not gender:
        gender_text = "女生"

    print("%s 是 %s" % (name, gender_text))


if __name__ == '__main__':
    print_info("张三")
    print_info("小红", gender=False)

输出结果:

张三 是 男生
小红 是 女生

注意:

  • 缺省参数必须在参数列表的末尾
  • 调用带有多个缺省参数的函数时需要指定参数的名称, 例如: printInfo(name=“xx”,age=19)

多值参数

有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
python 中有 两种 多值参数:

  • 参数名前增加 一个 * 可以接收 元组, 带星号(*)的参数不传参时默认是一个空的元组()。
  • 参数名前增加 两个 * 可以接收 字典, 带双星号(**)的参数不传值时默认是一个空的字典{}。

一般在给多值参数命名时,习惯使用以下两个名字:

  • *args —— 存放 元组 参数,前面有一个 *, 其中args 是 arguments 的缩写,有变量的含义
  • **kwargs —— 存放 字典 参数,前面有两个 *, 其中kw 是 keyword 的缩写,kwargs 可以理解为 键值对 参数

使用示例:

def demo(num, *args, **kwargs):
    print("参数1:", num)
    print("参数2:", args)
    print("参数3:", kwargs)


demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)

输出结果:

参数1: 1
参数2: (2, 3, 4, 5)
参数3: {'name': '小明', 'age': 18, 'gender': True}

多值参数的拆包

在调用多值参数的函数时,如果希望将一个 元组变量,直接传递给 args或者 将一个 字典变量直接传递给 kwargs,那就需要使用 拆包的方式传递:

  • 在 元组变量前,增加 一个 *
  • 在 字典变量前,增加 两个 *

例如:

def demo(*args, **kwargs):
    print("多值参数1:", args)
    print("多值参数2:", kwargs)


# 需要将一个元组变量/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}

print("错误的调用方式", "=" * 50)
# 会把 gl_nums 和 gl_xiaoming 作为元组传递给多值参数1: args
demo(gl_nums, gl_xiaoming)

print("正确的调用方式", "=" * 50)
demo(*gl_nums, **gl_xiaoming)

输出结果:

错误的调用方式 ==================================================
多值参数1: ((1, 2, 3), {'name': '小明', 'age': 18})
多值参数2: {}
正确的调用方式 ==================================================
多值参数1: (1, 2, 3)
多值参数2: {'name': '小明', 'age': 18}

由于元组和列表可以相互转换, 因此带*参数的函数也支持传入列表数据类型

例如:

def test(*parms):
    print("test参数parms类型:%s" % type(parms))
    print("test参数长度:%d" % len(parms))
    print("test参数内容:", parms)
    for p in parms:
        print("test参数parms每个元素遍历:", p)


def test2(parms):
    print("test2参数parms类型:%s" % type(parms))
    print("test2参数长度:%d" % len(parms))
    print("test2参数内容:", parms)
    for p in parms:
        print("test2参数parms每个元素遍历:", p)


if __name__ == '__main__':
    a = [1, 3, 5]
    b = (2, 4, 6)
    print("a的类型:%s" % type(a))
    print("b的类型:%s" % type(b))

    print("=============带星参数的test方法调用================")
    print("*" * 10, "test(a)", "*" * 10)
    test(a)  # 相当于传入的是([1, 3, 5],)
    print("*" * 10, "test(*a)", "*" * 10)
    test(*a)

    print("*" * 10, "test(b)", "*" * 10)
    test(b)  # 相当于传入的是((2, 4, 6),)
    print("*" * 10, "test(*b)", "*" * 10)
    test(*b)

    print("=============不带星参数的test2方法调用================")
    print("*" * 10, "test2(a)", "*" * 10)
    test2(a)

    print("*" * 10, "test2(b)", "*" * 10)
    test2(b)

输出结果:

a的类型:<class 'list'>
b的类型:<class 'tuple'>
=============带星参数的test方法调用================
********** test(a) **********
test参数parms类型:<class 'tuple'>
test参数长度:1
test参数内容: ([1, 3, 5],)
test参数parms每个元素遍历: [1, 3, 5]
********** test(*a) **********
test参数parms类型:<class 'tuple'>
test参数长度:3
test参数内容: (1, 3, 5)
test参数parms每个元素遍历: 1
test参数parms每个元素遍历: 3
test参数parms每个元素遍历: 5
********** test(b) **********
test参数parms类型:<class 'tuple'>
test参数长度:1
test参数内容: ((2, 4, 6),)
test参数parms每个元素遍历: (2, 4, 6)
********** test(*b) **********
test参数parms类型:<class 'tuple'>
test参数长度:3
test参数内容: (2, 4, 6)
test参数parms每个元素遍历: 2
test参数parms每个元素遍历: 4
test参数parms每个元素遍历: 6
=============不带星参数的test2方法调用================
********** test2(a) **********
test2参数parms类型:<class 'list'>
test2参数长度:3
test2参数内容: [1, 3, 5]
test2参数parms每个元素遍历: 1
test2参数parms每个元素遍历: 3
test2参数parms每个元素遍历: 5
********** test2(b) **********
test2参数parms类型:<class 'tuple'>
test2参数长度:3
test2参数内容: (2, 4, 6)
test2参数parms每个元素遍历: 2
test2参数parms每个元素遍历: 4
test2参数parms每个元素遍历: 6

函数的返回值

返回值 是函数 完成工作后,最后 给调用者的 一个结果,在函数中使用 return 关键字可以返回结果,调用函数一方,可以 使用变量 来 接收 函数的返回结果。

注意:return 表示返回,后续的代码都不会被执行

def sum_2_num(num1, num2):
    return num1 + num2


# 调用函数,并使用 result 变量接收计算结果
result = sum_2_num(10, 20)

print("计算结果是 %d" % result)

当函数内返回元组时,可以不需要书写一对()号,相当于函数可以返回多个值, 接收的时候也可以多个变量接收, 例如:

def measure():
    # 返回多个值,其实相当于返回元组
    return 10, 20, 30


# 接收多个值
a, b, c = measure()
print("a=%d,b=%d,c=%d" % (a, b, c))

输出结果:

a=10,b=20,c=30

函数的嵌套调用

一个函数里面 又调用 了 另外一个函数,这就是 函数嵌套调用,如果函数 test2 中,调用了另外一个函数 test1,那么执行到调用 test1 函数时,会先把函数 test1 中的任务都执行完,才会回到 test2 中调用函数 test1 的位置,继续执行后续的代码

def print_line(char, times):
    print(char * times)


def print_lines(char, times):
    row = 0

    while row < 5:
        # 调用另一个函数
        print_line(char, times)

        row += 1


print_lines("*", 5)

使用模块中的函数

什么是模块?
模块 就好比是 工具包,要想使用这个工具包中的工具,就需要 导入 import 这个模块
每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
在模块中定义的 全局变量 、 函数 都是模块能够提供给外界直接使用的工具

假设在module_01.py文件中定义了如下函数

def print_line(char, times):
    print(char * times)

想要在main.py中使用, 可以这样调用

import module_01  # 导入模块

module_01.print_line("*", 5)  # 通过模块名调用函数

或者这样调用

import module_01 as aaa  # 通过import...as...的方式给导入的模块命名

aaa.print_line("*", 5)  # 通过模块名调用函数

模块的变量调用类似.

9. 综合案例

实现一个控制台操作小程序,界面如下:
在这里插入图片描述
用户用数字选择不同的功能, 通过控制台可以录入用户信息, 实现增删改查的功能, 下面给出操作截图

  • 新建名片

  • 显示全部名片
    在这里插入图片描述
  • 查询和修改名片
    在这里插入图片描述
  • 删除名片
    在这里插入图片描述

实现思路:
使用 字典 记录 每一张名片 的详细信息, 然后使用 列表 统一记录所有的 名片字典

新建 cards_main.py 编写 主程序功能代码, 作为程序入口

import cards_tools

'''
**************************************************
                 欢迎使用【菜单管理系统】V1.0                 

                     1. 新建名片                      
                     2. 显示全部                      
                     3. 查询名片                      
                     0. 退出系统                      
**************************************************
'''
while True:
    cards_tools.show_menu()
    action = input("请选择操作功能:")
    print("您选择的操作是:%s" % action)`在这里插入代码片`
    if action in ["1", "2", "3", ]:
        if action == "1":
            cards_tools.new_card()
        elif action == "2":
            cards_tools.show_all()
        elif action == "3":
            cards_tools.search_card()
    elif action == "0":
        print("欢迎再次使用【名片管理系统】")
        break
    else:
        print("输入错误,请重新输入:")

新建 cards_tools.py 并在其中增加操作函数

# 所有名片记录的列表
card_list = []


def show_menu():
    """显示菜单
    """
    print("")
    print("*" * 50)
    print("欢迎使用【菜单管理系统】V1.0".center(50))
    print("")
    print("1. 新建名片".center(50))
    print("2. 显示全部".center(50))
    print("3. 查询名片".center(50))
    print("0. 退出系统".center(50))
    print("*" * 50)
    print("")


def new_card():
    """新建名片
    """
    print("-" * 50)
    print("当前操作:新建名片")

    # 提示用户输入信息
    name = input("请输入姓名:")
    phone = input("请输入电话:")
    qq = input("请输入qq:")
    card_dict = {"name": name, "phone": phone, "qq": qq}
    card_list.append(card_dict)
    print("成功添加 %s 的名片" % card_dict["name"])


def show_all():
    """显示全部名片
    """
    print("-" * 50)
    print("当前操作:显示全部名片")

    if len(card_list) == 0:
        print("提示:当前还没有任何名片记录,请先添加")
        return

    # 打印表头
    for title in ["姓名", "电话", "QQ"]:
        print(title, end="\t\t\t")

    # 打印名片信息
    print("\n", "=" * 50, sep="")
    for card_dict in card_list:
        print("%s\t\t%s\t\t%s" % (card_dict["name"], card_dict["phone"], card_dict["qq"]))


def search_card():
    """搜索名片
    """
    print("-" * 50)
    print("当前操作:搜索名片")

    # 提示要搜索的姓名
    find_name = input("请输入要搜索的姓名:")
    for card_dict in card_list:
        if card_dict["name"] == find_name:
            print("姓名\t\t\t电话\t\t\tQQ")
            print("-" * 40)
            print("%s\t\t\t%s\t\t\t%s" % (card_dict["name"], card_dict["phone"], card_dict["qq"]))
            print("-" * 40)
            edit_card(card_dict)
            break
    else:
        print("没有找到 %s" % find_name)


def edit_card(find_dict):
    """编辑信息
    """
    action_str = input("请选择要执行的操作 [1] 修改 [2] 删除 [0] 返回上级菜单:")
    if action_str == "1":
        find_dict["name"] = input("请输入姓名:")
        find_dict["phone"] = input("请输入电话:")
        find_dict["qq"] = input("请输入QQ:")
        print("%s 的名片修改成功" % find_dict["name"])
    elif action_str == "2":
        card_list.remove(find_dict)
        print("删除成功")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值