学习日志38 python

1 在Python中,标识符的命名规范

在 Python 中,标识符的命名需要遵循一定的规范,这些规范有助于提高代码的可读性和一致性。以下是主要的命名规则和惯例:

基本命名规则(必须遵守)

  1. 组成字符:只能包含字母(a-z, A-Z)、数字(0-9)和下划线(_)
  2. 首字符限制:不能以数字开头
  3. 大小写敏感name 和 Name 是两个不同的标识符
  4. 禁止使用关键字:不能使用 Python 的保留字(如ifforclass等)

命名惯例(建议遵循)

  1. 变量和函数:使用小写字母,多个单词之间用下划线连接(snake_case)

    python

    运行

    user_name = "Alice"
    def calculate_average(numbers):
        pass
    
  2. 类名:每个单词首字母大写,不使用下划线(PascalCase)

    python

    运行

    class UserAccount:
        pass
    
  3. 常量:全部使用大写字母,多个单词之间用下划线连接

    python

    运行

    MAX_SIZE = 100
    PI = 3.14159
    
  4. 私有成员:以单下划线开头(仅为约定,不真正真正限制访问)

    python

    运行

    class Person:
        def __init__(self):
            self._age = 20  # 私有变量约定
    
  5. 特殊方法和变量:以双下划线开头和结尾(用于 Python 内置功能)

    python

    运行

    def __init__(self):  # 构造方法
        pass
    
    __name__  # 模块特殊变量
    

  6. 避免使用单下划线作为标识符:虽然合法,但通常用于表示临时变量或不关心的变量

遵循这些命名规范可以使你的 Python 代码更加规范、易读,也便于其他开发者理解和维护。

【中文也可以吗?外文呢?】

在 Python 3 中,不仅中文,日文、韩文、俄语等其他语言的字符也可以作为标识符,因为 Python 3 对 Unicode 字符有全面支持,允许使用几乎所有语言的字母字符作为标识符的组成部分

例如,以下代码都是合法的:

python

运行

# 日文标识符
名前 = "田中"
def 挨う():
    print(f"こんにちは、{名前}さん")

# 韩文标识符
이름 = "김민수"
def 인사():
    print(f"안녕하세요, {이름}님")

# 俄语标识符
имя = "Иван"
def приветствие():
    print(f"Привет, {имя}!")

# 使用示例
挨拶()  # 输出:こんにちは、田中さん
인사()  # 输出:안녕하세요, 김민수님
приветствие()  # 输出:Привет, Иван!

不过,和中文标识符类似,这些语言的标识符在实际开发中也不推荐广泛使用,主要原因还是:

  • 团队协作中可能存在语言壁垒
  • 部分开发工具、字体或环境对非英文字符的支持可能存在问题
  • 不符合国际通用的编程规范和惯例

如果是特定语言环境下的个人项目,使用对应语言的标识符是可行的,但在多语言协作或开源项目中,建议仍以英文标识符为主,以保证代码的通用性和可维护性。

在 Python 3 中,允许使用中文作为标识符(包括变量名、函数名、类名等),这是 Python 3 对 Unicode 支持的一部分。

例如,以下代码是合法且可以正常运行的:

python

运行

# 中文变量名
姓名 = "张三"
年龄 = 25

# 中文函数名
def 打印信息():
    print(f"{姓名}的年龄是{年龄}岁")

# 中文类名
class 学生:
    def __init__(self, 学号):
        self.学号 = 学号

# 使用示例
打印信息()  # 输出:张三的年龄是25岁
s = 学生("2023001")
print(s.学号)  # 输出:2023001

不过,虽然语法上允许使用中文标识符,但在实际开发中通常不推荐,主要原因是:

  1. 兼容性问题:部分旧工具、编辑器或环境可能对中文标识符支持不佳
  2. 协作习惯:国际通用的编程规范中通常使用英文标识符,便于团队协作(尤其是有外籍成员的团队)
  3. 输入效率:中文输入可能比英文更繁琐,影响编码效率
  4. 命名一致性:混合中英文命名可能导致代码风格混乱

如果是个人项目或明确只有中文使用者的场景,使用中文标识符也无可厚非,但在正式项目或开源项目中,建议遵循英文命名的惯例。

2 Python支持使用星号(*)操作符对列表或元组进行参数解包(unpacking)

Python 中可以使用星号(*)操作符对列表、元组等可迭代对象进行参数解包(unpacking),这是一种非常实用的语法特性。

参数解包的主要用途是将可迭代对象中的元素 “展开”,作为独立的参数传递给函数,或者在数据结构中进行合并。

1. 函数调用时的参数解包

将列表 / 元组中的元素作为位置参数传递给函数:

python

运行

def sum(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
print(sum(*numbers))  # 等价于 sum(1, 2, 3),输出:6

# 元组也适用
numbers_tuple = (4, 5, 6)
print(sum(*numbers_tuple))  # 输出:15

2. 数据结构中的解包

在列表、元组等数据结构中合并元素:

python

运行

list1 = [1, 2, 3]
list2 = [4, 5, 6]

# 合并列表
combined = [*list1, *list2]
print(combined)  # 输出:[1, 2, 3, 4, 5, 6]

# 配合其他元素
new_list = [0, *list1, 4, *list2, 7]
print(new_list)  # 输出:[0, 1, 2, 3, 4, 4, 5, 6, 7]

3. 字典的解包(使用 **)

对于字典,可以使用 ** 解包键值对(作为关键字参数):

python

运行

def person_info(name, age, city):
    print(f"{name}, {age}岁, 来自{city}")

info = {"name": "Alice", "age": 25, "city": "Beijing"}
person_info(**info)  # 等价于 person_info(name="Alice", age=25, city="Beijing")

# 字典合并
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
combined_dict = {** dict1, **dict2}  # 后面的键会覆盖前面的
print(combined_dict)  # 输出:{'a': 1, 'b': 3, 'c': 4}

4. 星号在赋值中的使用(可迭代对象解包)

python

运行

# 提取部分元素
numbers = [1, 2, 3, 4, 5]
first, *middle, last = numbers
print(first)    # 1
print(middle)   # [2, 3, 4]
print(last)     # 5

# 忽略部分元素
*_, a, b = numbers
print(a, b)     # 4 5

星号解包语法让代码更简洁、灵活,尤其在处理动态参数或合并数据结构时非常方便。需要注意的是,* 只能用于可迭代对象,且在一个表达式中通常只能使用一次(除非是 Python 3.11 + 支持的多星号解包特定场景)。

3 作用域陷阱

执行以下代码,结果输出为()
num = 1
def fn():
num += 1
return lambda:print(num)
x = fn()
x()

A 报错
B 2
C None
D 1

正确答案:A

在函数fn()内部,执行num += 1时会报错,原因是:

  • 当在函数内部对变量进行赋值操作时,Python 会默认将该变量视为局部变量
  • 但此时num并未在fn()内部定义,而直接访问外部全局变量并尝试修改,就会触发UnboundLocalError

也就是说,函数执行到num += 1这一行就已经报错了,根本不会执行到返回 lambda 函数的步骤。

如果要修改全局变量num,需要在函数内用global声明:

python

运行

num = 1
def fn():
    global num  # 声明使用全局变量num
    num += 1
    return lambda: print(num)

x = fn()
x()  # 此时会输出2

因此原代码的执行结果是报错,正确答案为 A。

4 如何避免变量作用域的陷阱?

在 Python 中,避免变量作用域陷阱可以通过遵循一些明确的规则和最佳实践来实现。以下是具体方法:

1. 明确区分局部变量和全局变量

  • 局部变量:函数内部定义的变量,仅在函数内有效
  • 全局变量:函数外部定义的变量,作用域是整个模块

陷阱示例:在函数内修改全局变量却不声明

python

运行

count = 0  # 全局变量

def increment():
    count += 1  # 错误:试图修改全局变量但未声明

increment()  # 触发 UnboundLocalError

正确做法:使用global声明要修改的全局变量

python

运行

count = 0

def increment():
    global count  # 声明使用全局变量
    count += 1

increment()
print(count)  # 输出:1

2. 谨慎使用nonlocal处理嵌套作用域

当在嵌套函数中需要修改外层函数的变量时,使用nonlocal声明(区别于globalnonlocal用于非全局的外层变量)

陷阱示例:嵌套函数修改外层变量

python

运行

def outer():
    x = 10
    def inner():
        x = 20  # 这里实际创建了一个局部变量x,而非修改outer的x
    inner()
    print(x)  # 输出:10(未被修改)

outer()

正确做法:使用nonlocal声明

python

运行

def outer():
    x = 10
    def inner():
        nonlocal x  # 声明修改外层函数的x
        x = 20
    inner()
    print(x)  # 输出:20(已修改)

outer()

3. 避免变量名冲突

  • 不要在局部作用域中使用与全局变量相同的名字
  • 模块内的变量名避免与内置函数名冲突(如liststr等)

错误示例:变量名遮蔽内置函数

python

运行

def func():
    list = [1, 2, 3]  # 局部变量遮蔽了内置list
    return list(4, 5, 6)  # 错误:list已被重新定义为列表

func()  # 触发 TypeError

4. 使用类封装状态(替代全局变量)

过多的全局变量容易引发作用域混乱,使用类可以更好地管理状态

改进示例

python

运行

class Counter:
    def __init__(self):
        self.count = 0  # 实例变量,作用域清晰
    
    def increment(self):
        self.count += 1  # 明确修改当前实例的变量

c = Counter()
c.increment()
print(c.count)  # 输出:1

5. 遵循 "最小作用域原则"

  • 变量应在尽可能小的作用域内定义(如函数内部)
  • 避免在模块级别定义过多全局变量

推荐做法

python

运行

def process_data():
    # 临时变量在函数内部定义,避免污染全局作用域
    temp = [1, 2, 3]
    return sum(temp)

result = process_data()

6. 使用函数参数传递数据(而非依赖全局变量)

通过参数传递和返回值交换数据,使变量流向更清晰

改进示例

python

运行

# 不推荐:依赖全局变量
num = 10
def add(x):
    global num
    num += x

# 推荐:通过参数和返回值
def add(num, x):
    return num + x

num = 10
num = add(num, 5)  # 显式传递和接收,作用域明确

通过以上方法,可以有效避免绝大多数变量作用域问题。核心原则是:明确变量的定义位置和修改方式,减少对全局变量的依赖,让变量的作用范围清晰可见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值