Python-基础语法

Python简介

Python语言特点:简单、开源、解释性、面向对象、可移植性、可扩展性、可嵌入性、丰富的库

Python应用领域:Web开发、爬虫开发、云计算开发、人工智能、自动化运维、数据分析、科学计算

Python官网:https://www.python.org/

官方参考文档:https://docs.python.org/zh-cn/3.7/

菜鸟教程Python3:https://www.runoob.com/python3/python3-tutorial.html

安装Python

  1. 勾选Add Python 3.8 to PATH,点击Customize installation
  2. 默认,Next
  3. 勾选Install for all users,Customize install location选择安装路径(如:E:\Develop\Python\Python38),点击lnstall
  4. 点击Close
  • 若没勾选Add Python 3.8 to PATH,则手动配置环境变量,如:
    • E:\Develop\Python\Python38
    • E:\Develop\Python\Python38\Scripts

pip下载扩展库

pip命令说明
pip install xxx安装xxx模块
pip install -i 镜像URL xxx使用指定镜像安装xxx模块
pip install -U xxx升级xxx模块
pip uninstall xxx卸载xxx模块
pip list列出已安装的所有模块
pip list -o列出过时的模块

常用扩展库:

库名描述
matplotlib数据可视化
pymysql操作MySQL
pymongo操作MongoDB
scrapy爬虫框架
requests发送HTTP请求
beautifulsoup4解析HTML/XML
python-docx处理Word
xlrd读.xlsx和.xls

常用国内镜像:

清华大学开源软件镜像站:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package

文件头

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""说明:文件头只需在主文件加即可
#!在Linux系统中用于指定由哪个解释器来执行脚本,且必须在文件第1行

第1行指定使用Linux环境变量$PATH里某个路径下名为python的解释器来解释该python文件
第2行指定python解释器解释该文件时的编码格式
"""

注释

# 单行注释

"""
三个双引号的多行注释
"""

'''
三个单引号的多行注释
'''

输入输出

# input(prompt=None, /),input()得到的数据都是<class 'str'>
hello = input("提示信息:")

# print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
print(hello)
"""常用空白转义字符:
\r  回车,跳转到本行开头
\n  换行,跳转到当前位置的下一行
\t  水平制表,跳转到下一个Tab位置
\b  退格,跳转到前一列
\f  换页,跳转到下页开头
"""

示例:打印进度条

import time


def progress(percent=0.0, width=10, symbol='#'):
    """打印进度条"""
    if percent > 1:  # 若进度超过1,即100%,则按照100%处理
        percent = 1
    # 宽度% 个进度符号
    res = int(width * percent) * symbol
    # 结果进度条,'\r'表示回车到当前行行首,%-{width}s表示以左对齐且宽度为width
    progress_bar = f'\r[%-{width}s] %3d%%' % (res, int(percent * 100))
    # 打印该百分百的进度条,不换行
    print(progress_bar, end='')


if __name__ == "__main__":
    received_size = 0
    total_size = 2 * 1024 * 1024

    while received_size <= total_size:
        percent = received_size / total_size
        progress(percent, 50)

        received_size += 1024
        time.sleep(0.001)

运算符

# Python没有++和--

# 相当于math.pow(2, 3)
print(2 ** 3)
# 相当于math.floor(5 / 2)
print(5 // 2)

# 多变量赋值
i, f = 1, 1.0
print(i, f)

# 交换变量值
i, f = f, i
print(i, f)

# 打包
a = 1, 2, 3
print(a)  # (1, 2, 3)

# 解包
x, y, z = a  # 一一对应
print(x, y, z, sep=', ')  # 1, 2, 3
w, *tmp = a  # 取前面几个
print(w, tmp, sep=', ')  # 1, [2, 3]
*tmp, w = a  # 取后面几个
print(tmp, w, sep=', ')  # [1, 2], 3
x, y, z = {"a": 1, "b": 2, "c": 3}  # 解包字典默认取出的是key
print(x, y, z, sep=', ')  # a, b, c

# 多个比较运算符可以连续写
print(1 < 2 < 3)

# 逻辑运算符:不能使用&&、||
# 优先级:not > and > or
print(not 2 > 3)
print(1 < 2 and 2 > 3)
print(1 and 0)  # and连接两个数字时,有0则为0,否则为最后一个非0数字
print(1 < 2 or 2 > 3)
print(1 or 0)  # or连接两个数字时,全0则为0,否则为第一个非0数字

# 成员运算符
print(2 in {1, 2, 3})  # True
print("one" not in {"one": 1, "two": 2, "three": 3})  # False

# 位运算符
a = 60
b = 13
print("%16s" % bin(60))
print("%16s" % bin(13))
print("%16s" % bin(a & b))  # 按位与
print("%16s" % bin(a | b))  # 按位或
print("%16s" % bin(a ^ b))  # 按位异或
print("%16s" % bin(~a))  # 按位取反
print("%16s" % bin(a << 2))  # 左移
print("%16s" % bin(a >> 2))  # 右移

变量

Python变量三要素:id,type,值

Python中没有常量的概念

Python中变量以内容为基准而不是以变量名为基准

# 使用id()查看对象在内存中的地址
a = -5
b = -3 - 2
# 内容相同的变量,id相同
print(a, id(a), sep=', ')  # -5, 140723301062224
print(b, id(b), sep=', ')  # -5, 140723301062224
# 内容不同的变量,id不同
a = 256
print(a, b, id(a), sep=', ')  # 256, -5, 140723301070576

# is比较两个变量的id,==比较两个变量的值
a = [1, 2]
b = a
print(a is b)  # True
b = [1, 2]
print(a is b)  # False
print(a == b)  # True

# Python中的小整数池为[-5, 256],需要在shell环境下查看,在PyCharm中看不出来
>>> a = -5
>>> b = -5
>>> a is b
True
>>> a = -6
>>> b = -6
>>> a is b
False
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
>>>

判断对象是否存活-引用计数算法:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。

"""
垃圾收集算法:
分代收集理论
标记-清除算法
标记-复制算法
标记-整理算法
"""

# 增加引用
x = 10  # 栈区中的x指向堆区中的10,10的引用加1
y = x  # 栈区中的y指向堆区中的10,10的引用加1
z = x  # 栈区中的z指向堆区中的10,10的引用加1

# 减少引用
del x  # 删除栈区中的x
del y  # 删除栈区中的y
z = 20  # 将栈区中的z指向堆区中的20

数据类型

在Python中,类型属于对象,变量是没有类型的。

Python3中有6个标准的数据类型:

  1. 3个不可变(值变id变):number数值、string字符串、tuple元组
  2. 3个可变(值变id不变):list列表、dictionary字典、set集合

从语法上看,内置有__iter__方法的对象都是可迭代对象,如:字符串、元组、列表、字典、集合、open()打开的文件、等。

可迭代对象的__iter__方法会返回一个迭代器对象,迭代器对象可以使用__next__取值。

# 4种内置的数值类型:
print(type(1))  # <class 'int'>,Python中整型不会溢出
print(type(1.0))  # <class 'float'>
print(type(complex(1, 1)))  # <class 'complex'>
print(type(1 == 1))  # <class 'bool'>:值为True和False
# 隐式bool:0、None、空值(空字符串""、空列表[]、...)代表False,其余代表True

# 字符串类型:
print(type("1"))  # <class 'str'>

# 列表类型:类似Java的List
print(type([1]))  # <class 'list'>

# 元组类型:不可变的列表
# 定义只有一个元素的元组时,要在元素后面加上逗号
print(type((1,)))  # <class 'tuple'>

# 字典类型:key的特点同集合,定义格式类似JSON;类似Java的Map
print(type({1: 1}))  # <class 'dict'>

# 集合类型:元素类型为不可变类型,且元素无序、不重复;类似Java的Set
print(type({1}))  # <class 'set'>

# 数据类型转换:类型(变量)
s = '123'
i = int(s)
print(type(s))  # <class 'str'>
print(type(i))  # <class 'int'>
print(type(eval(s)))  # <class 'int'>

# 进制转换
print(bin(10))  # 0b1010
print(oct(10))  # 0o12
print(int(10))  # 10
print(hex(10))  # 0xa
print(int(10, 16))  # 0xa

1. 字符串

'单引号字符串'
"双引号字符串"
"""
三双引号允许一个字符串跨行,可以包含空白符及其他特殊字符
"""

print("01234" + "56789")  # 连接字符串
print("01234" * 5)  # 重复输出字符串

# 字符串有两种索引方式:从左往右以0开始;从右往左以-1开始
print("01234"[1])  # 通过索引获取字符串中的字符
print("01234"[0:-1])  # 切片[start, end)
print("01234"[0:-1:2])  # 切片[start, end),步长为2
print("01234"[4:0:-1])  # 切片[start, end),步长为-1
print("01234"[::-1])  # 字符串反转

print(r"hello\
world")  # 使用r或R可以让字符串保持原貌,反斜杠不发生转义

print("192.168.100.88".split('.', 2))  # 按指定字符从左往右切分2次:['192', '168', '100.88']
print("  \thello\n   ".strip())  # 返回删除头尾指定字符(默认为空白符)的字符串

print("192.168.100.88".rsplit('.', 2))  # 按指定字符从右往左切分2次:['192.168', '100', '88']
print("  \thello\n   ".lstrip())  # 相当于strip只去头
print("  \thello\n   ".rstrip())  # 相当于strip只去尾
print("HelloWorld".lower())  # 转为全小写
print("HelloWorld".upper())  # 转为全大写
print("HelloWorld".startswith("Hel"))  # 是否以指定字符串开头
print("HelloWorld".endswith("rld"))  # 是否以指定字符串结尾
print(".".join(['192', '168', '100', '88']))  # 将字符串序列按指定分隔符拼接:192.168.100.88
print("hello, java".replace('java', 'python'))  # 子串替换:hello, python
print("lhp".isalpha())  # 判断是否为字母
print(u"6".isdigit(), b"6".isdigit(), "六".isdigit(), "Ⅵ".isdigit())  # 判断是否为数字,能识别u"6"和b"6"
print(u"6".isnumeric(), "六".isnumeric(), "Ⅵ".isnumeric())  # 判断是否为数字,能识别u"6"、中文数字、罗马数字
print(u"6".isdecimal(), "六".isdecimal(), "Ⅵ".isdecimal())  # 判断是否为数字,能识别u"6"

print("01234".index("123"))  # 查找子串的索引,找不到抛异常
print("01234".find("123"))  # 查找子串的索引,找不到返回-1
print("lhp".center(9, '-'))  # 指定宽度,空位补-,居中
print("lhp".ljust(9, '-'))  # 指定宽度,空位补-,左对齐
print("lhp".rjust(9, '-'))  # 指定宽度,空位补-,右对齐
print("lhp".zfill(9))  # 指定宽度,默认空位补0,右对齐
print("hello\tworld".expandtabs(2))  # 指定制表符宽度
print("heLLo world".swapcase())  # 大小写反转
print("heLLo world".capitalize())  # 字符串首字母大写,其余小写
print("heLLo world".title())  # 每个单词变成大驼峰
字符编码

Python2默认编码为ascii,Python3默认编码为utf-8

计算机系统中字符编码的通用工作方式:在计算机内存中,统一使用Unicode,当需要将字符保存到硬盘或者需要传输字符时,就将它们转换为utf-8。Python3的字符串在内存中以Unicode表示。

  1. Python2中字符串进行编码转换的过程为:字符串–>decode(‘原来的字符编码’)–>Unicode字符串–>encode(‘新的字符编码’)–>字符串
  2. Python3中字符串默认为Unicode,因此不需要先解码为Unicode,其进行编码转换的过程为:字符串–>encode(‘新的字符编码’)–>字符串

解决python2乱码问题:文件头的# coding:与文件存储的编码格式一致、字符串前加上u

解决python3乱码问题:文件头的# coding:与文件存储的编码格式一致

zh = "中国".encode('utf-8')
print(zh)  # b'\xe4\xb8\xad\xe5\x9b\xbd'
print(type(zh))  # <class 'bytes'>
print(zh.decode())  # 中国

# ord获取字符的整数表示
print(ord('中'))  # 20013
# chr把编码转换为字符
print(chr(20013))  # 中
字符串格式化
# %格式化
str1 = "%s" % {"name": "lhp"}  # {'name': 'lhp'}
str2 = "%(name)s" % {"name": "lhp"}  # lhp

# str.format()格式化
str3 = "{}, {}".format("hello", 2021)  # hello, 2021
str4 = "{1}, {0}".format("hello", 2021)  # 2021, hello
str5 = "{name}, {age}".format(name="lhp", age=21)  # lhp, 21

# f{表达式}格式化
str6 = f"hello, {str2}"  # hello, lhp

2. 列表

# 创建列表
list1 = list("hello")  # 转为列表
print(list1)  # ['h', 'e', 'l', 'l', 'o']
list1 = ['1', 1, 1.0]
print(list1)  # ['1', 1, 1.0]

print(list1 + [2])  # 连接两个列表,获取一个新列表
print(list1 * 2)  # 复制列表n次,获取一个新列表
print([1, 2] < [1, 3])  # 类似于字符串比较大小:True

print(list1[-1])  # 通过索引获取元素,索引越界则抛异常:1.0
print(list1[0:-1])  # 切片:['1', 1]

# 增
list1[len(list1):] = (3, 4)  # 等同于list1.extend((3, 4))
print(list1)  # ['1', 1, 1.0, 3, 4]
list1.append(5)
print(list1)  # ['1', 1, 1.0, 3, 4, 5]
list1.insert(1, "a")
print(list1)  # ['1', 'a', 1, 1.0, 3, 4, 5]

# 删
del list1[1]
print(list1)  # ['1', 1, 1.0, 3, 4, 5]
print(list1.pop())  # 根据索引删除,默认最后一个
print(list1)  # ['1', 1, 1.0, 3, 4]
list1.remove('1')  # 根据元素值删除
print(list1)  # [1, 1.0, 3, 4]

# 查
print(list1.index(3))  # 查找指定索引值
print(list1.count(1))  # 统计指定值有多少个

list1.clear()  # 清空列表
print(list1)  # []

list1 = [5, 4, 6]
list1.reverse()  # 列表反转
print(list1)  # [6, 4, 5]

list1.sort(reverse=True)  # 降序排序,元素必须是同一类型
print(list1)  # [6, 5, 4]

# 列表当做队列
list1.append("hello")  # 入队
list1.pop(0)  # 出队

# 列表当做栈
list1.append("world")  # 入栈
list1.pop()  # 出栈

3. 元组

# 创建元组
tuple1 = (1,)

# 类型转换
tuple1 = tuple([4, 3, 2, 1, 0])
print(tuple1)  # (4, 3, 2, 1, 0)
# 索引取值
print(tuple1[0], tuple1[-1])  # 4 0
# 切片
print(tuple1[0:-1])  # (4, 3, 2, 1)
# 查找元素索引
print(tuple1.index(2))  # 2
# 统计元素数量
print(tuple1.count(2))  # 1

4. 字典

# 创建字典
# 方式1
dict1 = {"name": "zs", "age": 18}
print(dict1)  # {'name': 'zs', 'age': 18}
print(type({}))  # {}默认为字典
# 方式2
print(dict(name="zs", age=18))  # {'name': 'zs', 'age': 18}
# 方式3
student = [
    ['name', 'zs'],
    ('age', 18)
]
print(dict(student))  # {'name': 'zs', 'age': 18}
# 方式4
print({}.fromkeys(['name', 'age']))  # {'name': None, 'age': None}

# 根据key操作
print(dict1.get('sex'))  # 按key取值,key不存在返回None
print(dict1.get('sex', 'default'))  # 按key取值,key不存在返回默认值(第二个参数)
print(dict1['age'])  # 按key取值,key不存在则抛异常
dict1['age'] = 20  # key存在,则修改
dict1['sex'] = '男'  # key不存在,则创建
print(dict1)  # {'name': 'zs', 'age': 20, 'sex': '男'}
# in和not in判断的是字典的key
print('age' in dict1)  # True
# 获取所有键
print(dict1.keys())  # dict_keys(['name', 'age', 'sex'])
# 获取所有值
print(dict1.values())  # dict_values(['zs', 20, '男'])
# 获取所有键值对
print(dict1.items())  # dict_items([('name', 'zs'), ('age', 20), ('sex', '男')])
# 删除
del dict1['sex']
print(dict1)  # {'name': 'zs', 'age': 20}
tuple1 = dict1.popitem()  # 以后进先出的顺序删除,并以元组形式返回键值对;如果字典为空则抛异常
print(dict1, tuple1)  # {'name': 'zs'} ('age', 20)
name = dict1.pop('name')
print(name, dict1)  # zs {}

dict1 = {"name": "zs", "age": 18}
dict1.update({"name": "zs", "age": 20, "sex": "男"})
print(dict1)  # {'name': 'zs', 'age': 20, 'sex': '男'}

# 如果key存在,则返回原key
print(dict1.setdefault('name', 'ls'), dict1)  # zs {'name': 'zs', 'age': 20, 'sex': '男'}
# 如果key不存在,则添加,并返回添加的值
print(dict1.setdefault('height', 175), dict1)  # 175 {'name': 'zs', 'age': 20, 'sex': '男', 'height': 175}

dict1.clear()
print(dict1)  # {}

5. 集合

# 创建集合
set0 = set()  # 定义空集合:set()
set1 = {1, 2, 3}
set2 = {2, 3, 4}
print(set0, set1, set2)  # set() {1, 2, 3} {2, 3, 4}

print(set1 & set2)  # 交集:{2, 3}
print(set1 | set2)  # 并集:{1, 2, 3, 4}
print(set1 - set2)  # 差集:{1}
print(set1 ^ set2)  # 并集-交集:{1, 4}

set1 = {1}
set2 = {1}
print(set1 > set2)  # set2是否为set1的真子集:False
print(set1 >= set2)  # set2是否为set1的子集:True
print(set1.issuperset(set2))  # set2是否为set1的子集:True
print(set2.issubset(set1))  # set2是否为set1的子集:True

# 删
set1 = {1, 2, 3}
set1.discard(4)  # 删除元素,不存在不抛异常,无返回
set1.remove(3)  # 删除元素,不存在抛异常,无返回
print(set1)  # {1, 2}
print(set1.pop(), set1)  # 1 {2}
# 改
set1.update({3, 4})
print(set1)  # {2, 3, 4}
# 增
set1.add(1)
print(set1)  # {1, 2, 3, 4}

# 两个集合是否没有交集
print(set1.isdisjoint({4, 5, 6}))  # False
# 等同于set1 = set1 - {3, 4, 5}
set1.difference_update({3, 4, 5})
print(set1)  # {1, 2}
# 等同于set1 - {2}
print(set1.difference({2}), set1)  # {1} {1, 2}

推导式

1. 列表推导式

"""列表推导式格式:
[生成列表元素的表达式 for 表达式中的变量 in 变量要遍历的序列]
[生成列表元素的表达式 for 表达式中的变量 in 变量要遍历的序列 if 过滤条件]

for可以有多个;执行时,会先使用for迭代出变量,然后将生成列表元素的表达式应用于变量
"""

list1 = []
for i in range(3):
    if i % 2 == 0:
        for j in range(2):
            list1.append((i, j))
print(list1)  # [(0, 0), (0, 1), (2, 0), (2, 1)]

# 以下列表推导式等同于以上for循环
list2 = [(i, j) for i in range(3) if i % 2 == 0 for j in range(2)]
print(list2, type(list2))  # [(0, 0), (0, 1), (2, 0), (2, 1)] <class 'list'>

2. 生成器推导式

# 生成器推导式返回一个生成器对象,而不是元组
tuple1 = (s for s in "012301")
# <generator object <genexpr> at 0x00000219AE45B5F0> <class 'generator'>
print(tuple1, type(tuple1))
# ('0', '1', '2', '3', '0', '1')
print(tuple(tuple1))

3. 字典推导式

list1 = [i for i in range(10, 13)]
list2 = [i for i in range(20, 23)]
# 字典推导式
dict1 = {list1[i]: list2[i] for i in range(min(len(list1), len(list2)))}
print(dict1, type(dict1))  # {10: 20, 11: 21, 12: 22} <class 'dict'>

4. 集合推导式

# 集合推导式
set1 = {s for s in "012301"}
print(set1, type(set1))  # {'2', '1', '0', '3'} <class 'set'>

流程控制

1. if

语法:

if 条件:
    语句
elif 条件:
    语句
else:
    语句

例子:

import random

left = -1
right = 1
num = random.randint(left, right)  # 范围:[left, right]
if num > 0:
    print(num, '> 0')
elif num < 0:
    print(num, '< 0')
else:
    print(num, '= 0')

三目运算符:

# 三目运算符不能使用:条件 ? 条件成立执行的表达式 : 条件不成立执行的表达式
# 三目运算符格式:条件成立执行的表达式 if 条件 else 条件不成立执行的表达式
print(1 if 1 > 2 else 2)

Python没有switch

2. while

语法:

while 条件:
    条件为True时执行的语句
# 以下可以没有
else:
    条件为False且没有被break时执行的语句

例子:

counter = 0
while True:
    if counter < 5:
        print("continue:", counter)
        counter += 2
        continue
    if counter > 10:
        print("break:", counter)
        break
    print(counter)
    counter += 1
else:
    # 条件永远为True,因此不会执行
    print(counter)

itr = "01234".__iter__()
while True:
    try:
        print(itr.__next__())
    except StopIteration:
        break

3. for

语法:

for 迭代变量 in 序列:
    语句
# 以下可以没有
else:
    穷尽序列且没有被break时执行的语句

例子:

"""for循环也称迭代器循环,其工作原理为:
1. [7, 8, 9].__iter__()获取一个迭代器对象
2. 重复:i = 迭代器对象.__next__()
3. for循环捕捉到StopIteration异常后结束
"""
for i in [7, 8, 9]:
    print(i)
    if i == 8:
        print("容器中的元素数量:", len([7, 8, 9]))
        break
else:
    print("else")

print("生成[start, stop)的数字,步长为step:")
for i in range(0, 10, 2):
    print(i, end='-')  # 0-2-4-6-8-

print("\n生成一个可遍历容器的(索引, 元素):")
for i in enumerate([7, 8, 9], 1):
    print(i, end='-')  # (1, 7)-(2, 8)-(3, 9)-

函数

定义语法:

# 函数需要先被调入内存,才能被调用
def 函数名(形参):
    """
    文档注释
    """
    函数体
    return 返回值

Python中的函数可以看做是一个对象,函数可以出现在任何对象可以出现的地方,如:赋值、参数、返回值、容器中的元素、等。

Python允许嵌套定义函数,即在一个函数中定义另一个函数。

1. 参数类型

位置参数:位置参数须以正确的顺序传入函数,且调用时的数量必须和声明时的一样

def add(left, right):
    return left + right

add(1, 2)  # 正确调用
add()  # 调用函数时不加参数会抛异常

关键字实参:函数调用使用关键字参数来确定传入的参数值,允许函数调用时参数的顺序与声明时不一致

def add(left, right):
    return left + right

add(right=2, left=1)  # 关键字实参

默认形参:调用函数时,如果没有传递参数,则会使用默认参数

def add(left, right=0):  # 默认形参必须在位置参数之后
    return left + right

add(1)

可变长度参数:函数调用时,实参数量可变

  1. 加了星号*的参数会以元组的形式导入,存放所有未命名的变量参数;如果在函数调用时没有指定参数,它就是一个空元组;星号后的其他参数必须用关键字传入

    def add(*args, increment):
        print(type(args))  # <class 'tuple'>
        print(args, increment)
    
    add(1, 2, increment=1)  # (1, 2) 1
    add([1, 2], increment=1)  # ([1, 2],) 1
    
  2. 加了两个星号**的参数会以字典的形式导入;两个星号后不能再有其他参数

    def add(increment, **kwargs):  # 可变长度关键字实参
        print(type(kwargs))  # <class 'dict'>
        print(increment, kwargs)
    
    add(1, name='zs', age=18)  # 1 {'name': 'zs', 'age': 18}
    

序列解包参数:Python解释器会自动将序列解包成多个元素,并一一传递给各个位置参数

def info(age, *args, **kwargs):
    print(age, args, kwargs)

# 序列解包:以下相当于info(1, 2)
info(*(1, 2))  # 1 (2,) {}
# 字典解包:以下相当于info(name="zs", age=18)
info(**{'name': 'zs', 'age': 18})  # 18 () {'name': 'zs'}

函数的形参也可以是函数,调用时传递函数名作为实参;类似C语言的函数指针:

def add(left, right, func=round):  # 回调函数
    left = func(left)
    right = func(right)
    print(left, "+", right, "=", left + right)

add(-1.5, 10.51)  # -2 + 11 = 9
add(-1, 10, abs)  # 1 + 10 = 11

函数参数的提示信息:仅仅起提示作用,需要Python3.5+:

"""参数类型提示格式:
def 函数名(参数: 提示信息 = 默认值, 参数: 提示信息 = 默认值) -> 返回值的提示信息:
    pass
"""


def info(name: str = "zs", age: int = 18) -> str:
    return name + str(age)

print(info("ls", 19))  # ls19
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
print(info.__annotations__)


def information(name: "提示信息", age: "提示信息" = 18) -> "返回值的提示信息":
    return name + str(age)

print(information('ww', 20))  # ww20
# {'name': '提示信息', 'age': '提示信息', 'return': '返回值的提示信息'}
print(information.__annotations__)

2. 装饰器-装饰模式

闭包就是能够读取其他函数内部变量的函数,可以理解成“定义在一个函数内部的函数”。闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰,直观的说就是形成一个不销毁的栈环境。

Python装饰器类似于装饰模式,但Python装饰器可以更方便地更改函数和方法。

无参装饰器:

# 序列化装饰器
def serializable(func):
    # 闭包函数wrapper,使用了serializable函数的内部变量func
    def wrapper(*args, **kwargs):
        print("序列化开始->", end="")
        result = func(*args, **kwargs)
        print("->序列化结束", end="")
        return result
    return wrapper  # 返回装饰对象

# 缓冲装饰器
def buffered(func):
    # 闭包函数wrapper,使用了buffered函数的内部变量func
    def wrapper(*args, **kwargs):
        print("缓冲开始->", end="")
        result = func(*args, **kwargs)
        print("->缓冲结束", end="")
        return result
    return wrapper  # 返回装饰对象

def input_stream():
    print("输入", end="")

input_stream = buffered(input_stream)
input_stream()  # 缓冲开始->输入->缓冲结束
print('\n', input_stream.__name__)  # wrapper
input_stream = serializable(input_stream)
input_stream()  # 序列化开始->缓冲开始->输入->缓冲结束->序列化结束
print()


# 语法糖:@名称;会自动给名称后加上(),然后将@下的函数作为参数传递到()
# 以下相当于serializable(buffered(input_stream))
@serializable
@buffered
def output_stream():
    print("输出", end="")

output_stream()  # 序列化开始->缓冲开始->输出->缓冲结束->序列化结束

有参装饰器:

# 缓冲装饰器
def config(system="linux", encoding="utf-8"):
    def buffered(func):
        # 闭包函数wrapper,使用了buffered函数的内部变量func
        def wrapper(*args, **kwargs):
            print("{系统:%s, 编码:%s}->" % (system, encoding), end="")
            print("缓冲开始->", end="")
            result = func(*args, **kwargs)
            print("->缓冲结束", end="")
            return result
        return wrapper  # 返回装饰对象
    return buffered  # 返回装饰器

def input_stream():
    print("输入", end="")

buffered = config(system="windows", encoding="gbk")  # 先配置一个装饰器
input_stream = buffered(input_stream)  # 然后装饰input_stream
input_stream()  # {系统:windows, 编码:gbk}->缓冲开始->输入->缓冲结束
print("\n", input_stream.__name__)  # wrapper


"""语法糖:
@config(system="windows", encoding="gbk")相当于:
1. 调用config(system="windows", encoding="gbk")获取一个配置好的装饰器,得到@buffered
2. @buffered相当于调用buffered(input_stream)获取一个装饰对象
3. 用装饰对象代替output_stream
"""
@config(system="windows", encoding="gbk")
def output_stream():
    print("输出", end="")

output_stream()  # {系统:windows, 编码:gbk}->缓冲开始->输出->缓冲结束

functools.wraps:

from functools import wraps  # 导入functools.wraps()

# 缓冲装饰器
def buffered(func):
    # 闭包函数wrapper,使用了buffered函数的内部变量func
    @wraps(func)
    def wrapper(*args, **kwargs):
        """
        wrapper被@wraps(func)作用后:
        wrapper的__module__、__name__、__qualname__、__doc__、__annotations__、等会修改成func的
        """
        print("缓冲开始->", end="")
        result = func(*args, **kwargs)
        print("->缓冲结束", end="")
        return result
    # wrapper.__name__ = func.__name__
    return wrapper  # 返回装饰对象

@buffered
def input_stream():
    print("输入", end="")

input_stream()  # 缓冲开始->输入->缓冲结束
print("\n", input_stream.__name__)  # input_stream

3. 生成器

在Python中,使用了yield的函数被称为生成器。生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个自定义的迭代器;在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。

# 生成器示例1:
def my_range(start, stop, step=1):
    while start < stop:
        yield start  # 返回start
        start += step

itr = my_range(1, 5, 2)
print(itr.__next__())  # 1
print(next(itr))  # 3
print(next(itr))  # StopIteration


# 生成器示例2:
def add_to_list():
    result = []
    while True:
        ele = yield result  # 返回result
        result.append(ele)

add_iter = add_to_list()
# 先让函数中断在ele = yield result
print(next(add_iter))  # []
# list_iter.send("Java")相当于唤醒函数并返回一个"Java",即ele = "Java"
print(add_iter.send("Java"))  # ['Java']
# 相当于add_iter.send(None)
print(next(add_iter))  # ['Java', None]
# 结束函数add_to_list
add_iter.close()

4. lambda-匿名函数

Python使用lambda表达式来创建匿名函数:

"""
lambda语法:lambda [arg1[, arg2, .....argn]]: expression

注意:
1. 表达式的计算结果相当于函数的返回值
2. lambda会返回一个函数对象
"""

myMax = lambda a, b: a if a > b else b
print(myMax)  # <function <lambda> at 0x000001F2159DD4C0>
print("myMax(10, 20) =", myMax(10, 20))  # myMax(10, 20) = 20

students = [
    {"name": "zs", "age": 13},
    {"name": "ls", "age": 14}
]
# 按照age降序排序
students.sort(key=lambda student: student['age'], reverse=True)
print(students)  # [{'name': 'ls', 'age': 14}, {'name': 'zs', 'age': 13}]

5. 常用内置函数

map(func, *iterables):有几个序列,func就是几元函数

  1. 只有1个序列:将func依次作用于这个序列的每一个元素上,得到一个新的序列
  2. 多余1个序列:将每个序列同一位置的元素同时传递给func,把得到的每一个返回值存放在一个新的序列中;若多个序列的元素数量不同,则根据数量最少的进行求解
result = map(lambda x: x ** 2, [1, 2, 3])
print(list(result))  # [1, 4, 9]

result = map(lambda x, y: x + y, [1, 2, 3], [10, 20])
print(list(result))  # [11, 22]

filter(function or None, iterable):序列的每个元素作为参数传递给function进行判断,若返回True则保留该元素,若返回False则过滤掉该元素

result = filter(lambda x: x % 2 == 0, [0, 1, 2, 3, 4])
print(list(result))  # [0, 2, 4]

reduce(function, sequence[, initial]):function必须是二元函数;先计算function(initial, sequence[0]),然后计算function(上一个计算结果, sequence[1]),以此类推

import functools

print(functools.reduce(lambda a, b: a + b, [1, 2, 3]))  # 6
print(functools.reduce(lambda a, b: a + b, [1, 2, 3], 10))  # 16

命名空间和作用域

命名空间是从名称到对象的映射,大部分的命名空间都是通过字典来实现的。

  1. 内置名称:Python内置的名称
  2. 全局名称:模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量
  3. 局部名称:函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量

变量查找顺序:局部命名空间–>全局命名空间–>内置命名空间

命名空间的生命周期:取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束

作用域就是一个Python程序可以直接访问命名空间的正文区域:

  1. local:当前的,包含局部变量,如一个函数/方法内部
  2. enclosing:当前外的局部(例如闭包),如一个函数(或类)A里包含了一个函数B,那么对于B中的名称来说,A中的作用域就为nonlocal
  3. global:当前脚本的最外层,比如当前模块的全局变量
  4. built-in:内置的变量/关键字等

变量查找顺序:(local–>enclosing)局部–>(global)全局–>(built-in)内置

内部作用域中修改外部作用域的变量:

global_var = 1


def enclosing():
    global global_var  # 修改global中的变量时,需要使用global声明
    global_var = 2
    print("in-global_var =", global_var)  # in-global_var = 2

    enclosing_var = 3

    def local():
        nonlocal enclosing_var  # 修改enclosing中的变量时,需要使用nonlocal声明
        enclosing_var = 4
        print("in-enclosing_var =", enclosing_var)  # in-enclosing_var = 4

    local()
    print("out-enclosing_var =", enclosing_var)  # out-enclosing_var = 4


enclosing()
print("out-global_var =", global_var)  # out-global_var = 2

异常

常见异常:

  1. Exception:异常的基类
  2. TypeError:参数类型不合适
  3. ValueError:参数值不正确(类型正确)
  4. NameError:找不到全局名称
  5. IndexError:序列索引超出范围
  6. KeyError:找不到对应的键
  7. AttributeError:找不到属性
  8. ZeroDivisionError:除法或取模运算的第二个参数为零

try处理异常格式:

# try必须有,except和finally只能省略一个,else可有可无
try:
    可能出现异常的代码
except 异常类名 as 别名:  # 也可以不加:as 别名
    异常的处理代码
else:
    没有异常发生时执行的代码块
finally:
    必须执行的代码

raise抛出异常格式:

raise 异常类名

自定义异常:继承内置异常类,如Exception

异常处理示例:

# 示例1
try:
    print("可能出现异常的代码")
    1 / 0
except Exception as e:
    print("异常的处理代码")
else:
    print("没有异常发生时执行的代码块")
finally:
    print("必须执行的代码")

# 示例2
try:
    1 / 0
except (Exception, ZeroDivisionError):
    pass

# 明确抛出异常示例
raise AssertionError

断言:

# 如果表达式为False,则抛出AssertionError
assert 表达式

# 等价于
if not 表达式:
    raise AssertionError

深浅拷贝

直接赋值:赋值得到的是对象的引用(相当于给对象起别名)

浅拷贝:对象中的值类型成员变量拷贝值,引用类型成员变量仅拷贝引用

深拷贝:对象中的值类型成员变量拷贝值,引用类型成员变量拷贝整个引用对象

# 浅拷贝
a = {1: [1, 2, 3]}
b = a.copy()
print(a, b)  # {1: [1, 2, 3]} {1: [1, 2, 3]}
a[1][0] = 0
print(a, b)  # {1: [0, 2, 3]} {1: [0, 2, 3]}

# 深拷贝,需要导入copy模块
import copy

c = copy.deepcopy(a)
print(a, c)  # {1: [0, 2, 3]} {1: [0, 2, 3]}
a[1][0] = 1
print(a, c)  # {1: [1, 2, 3]} {1: [0, 2, 3]}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值