Python进阶笔记(二)
Python中“*”的功能
Python中*
最常用的便是作为一个运算符号使用,但其实*
也可用于实现重复、打包、解包功能。
1
2
3
4
|
print("hahaha" * 3)
# hahahahahahahahaha
print([1, 2, 3] * 3)
# [1, 2, 3, 1, 2, 3, 1, 2, 3]
|
1
2
3
4
5
6
|
numbers = [1, 2, 3, 4, 5]
first, *rest = numbers
print(first)
print(rest)
# 1
# [2, 3, 4, 5]
|
还有一个典型的例子便是返回yaml文件的值时,如果不需要用到全部值,可以使用这个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import yaml
def config_read():
config_path = "./configs/config.yaml"
fo = open(config_path, 'r', encoding='utf-8')
res = yaml.load(fo, Loader=yaml.FullLoader)
return res
def return_config():
config = config_read()
return (config["LOG"], config["SERVER"], config["DEVICE"])
first, *_ = return_config()
print(first)
print(_)
# {'LOG_LEVEL': 'DEBUG', 'LOG_FOLDER': './logs', 'LOG_FILENAME': 'service.log', 'BACKUPCOUNT': 5}
# [{'IP': '0.0.0.0', 'PORT': 10004, 'WORKERS': 1}, 'mps']
|
可以看到当前不需要的参数暂时会被存入_
中。
在函数中有一个常用的作法是使用args
,所有传入的参数都会被打包成一个列表:
1
2
3
4
5
6
|
def print_values(*args):
for arg in args:
print(arg)
print(1, "2", "都是借口")
# 1 2 都是借口
|
那么说到args
,就不得不说kwargs
了,它通常配合**
使用:
1
2
3
4
5
6
7
8
9
10
11
|
def example(**kwargs):
for key, value in kwargs.items():
print(f"{key} = {value}")
example(a=1, b=2, c=3)
# a = 1
# b = 2
# c = 3
example(1, 2, 3)
# 注意这种写法会报错,因为kwargs必须传入字典
|
*
的解包功能,与打包相反,它会将一系列的值打开(类似于解压),比如列表可以使用*
解包,字典可以使用**
解包。
1
2
3
4
5
6
7
8
9
10
11
12
|
def greet(name, age):
print(f"hello {name}, you are {age} years old.")
person = ("Alice", 30)
greet(*person)
# hello Alice, you are 30 years old.
a = [1, 2, 3]
b = (3, 4, 5)
c = [*a, *b]
print(c)
# [1, 2, 3, 3, 4, 5]
|
可以看到它甚至可以合并元组和列表,因为*
对列表和元组的解包都生效。再看**
的例子:
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
|
def create_profile(name, age, email):
print(f"name: {name}")
print(f"age: {age}")
print(f"email: {email}")
dict_1 = {
"name": "Jarson Cai",
"age": 18,
"email": "jarsoncai@qq.com"
}
create_profile(**dict_1)
# name: Jarson Cai
# age: 18
# email: jarsoncai@qq.com
dict_2 = {"tall": 180}
dict_3 = {**dict_1, **dict_2}
list_3 = [*dict_1, *dict_2]
set_3 = {*dict_1, *dict_2}
print(dict_3)
# {'name': 'Jarson Cai', 'age': 18, 'email': 'jarsoncai@qq.com', 'tall': 180}
print(list_3)
# ['tall', 'name', 'email', 'age']
print(set_3)
# {'tall', 'name', 'email', 'age'}
|
**
在传入参数重可以解包,在合并字典中也可以解包,同样在使用*
对字典进行解包时,默认会将所有的key解包出来作为一个值。
装饰器
装饰器(Decorator)是Python中一种非常强大且灵活的工具,用于修改或增强函数或方法的行为。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。
举一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
|
def square(x):
return x*x
def print_running(f, x):
print(f"{f.__name__} is running")
return f(x)
result = print_running(square, 2)
print(result)
# square is running
# 4
|
它在这里的作用是不改变函数的情况下,增加了函数运行的提示。同样的功能可以使用装饰器来实现,我们在原有的基础上再增加一个测量时间的功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def square(x):
return x*x
import time
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{func.__name__} is running")
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} execution time: {end_time - start_time}")
return result
return wrapper
decorator_square = decorator(square)
decorator_square(10)
# square is running
# square execution time: 9.5367431640625e-07
|
而python中定义了一个更为简单的方式来使用装饰器吗,就是直接给函数戴一个装饰器的帽子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import time
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{func.__name__} is running")
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} execution time: {end_time - start_time}")
return result
return wrapper
@decorator
def square(x):
return x*x
square(10)
|
上述的写法效果与前面相同。
但是判断不同函数运行时间是否超过阈值的功能虽然常见,但它往往是变化的,也就是说需要的阈值会不同,这样我们可以再套一层定义一个装饰器生成器,来看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import time
def timer_decorator(threshold):
def time_calculate(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
if end_time - start_time > threshold:
print(f"{func.__name__} took longer than {threshold}")
return result
return wrapper
return time_calculate
@timer_decorator(0.3)
def time_sleep():
time.sleep(0.4)
time_sleep()
print(time_sleep.__name__)
# time_sleep took longer than 0.3
# wrapper
|
上述代码将装饰器变为了可定义的装饰器,便于使用,但是使用了装饰器后的函数名会发生改变,我们需要使用一种特殊的方法进行继承。
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
|
import time
import functools
def timer_decorator(threshold):
def time_calculate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
if end_time - start_time > threshold:
print(f"{func.__name__} took longer than {threshold}")
return result
return wrapper
return time_calculate
@timer_decorator(0.3)
def time_sleep():
time.sleep(0.4)
time_sleep()
print(time_sleep.__name__)
# time_sleep took longer than 0.3
# time_sleep
|
上述代码中使用python中自带的装饰器@functools.wraps
。
总的来说,装饰器有许多优点:
- 提升代码复用性,避免冗余。
- 使用装饰器可以保证一个复杂的函数逻辑清晰,减少代码查看量。
- 通过装饰器,可以扩展别人的函数,在添加额外的行为时,不会修改原函数的逻辑。
使用dotenv模块存储敏感信息
在项目中,我们通常会用到一些数据库的密码,以及大模型相关的key信息,然而如果我们将这些信息直接写在代码中,很容易造成泄露。
我们可以使用python-dotenv
模块来解决这个问题。首先下载这个模块:
1
|
pip install python-dotenv
|
在项目目录下新建一个.env
文件,将敏感信息存入其中:
1
2
|
OPENAI_API_KEY = "FAKE_OPENAI_API_KEY"
DB_PASSWORD = "FAKE_DB_PASSWORD"
|
通过代码来读取:
1
2
3
4
5
6
7
|
from dotenv import load_dotenv
# 将文件中的环境变量变为进程中的环境变量
load_dotenv()
import os
openai_api_key = os.getenv("OPENAI_API_KEY")
db_password = os.getenv("DB_PASSWORD")
|
当然也可以将项目的名称加入.env文件中来进行区分:
1
2
3
|
from dotenv import load_dotenv
# 读取的时候也需要加入改变后的名字
load_dotenv("projectA.env")
|
如果需要上传git仓库,则需要加好.gitignore
:
*.env
最后修改于 2024-07-01
本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。