Python 开发指南:重要语法IF、断言、异常捕获、with 关键字实现资源开闭

2023年 7月 12日 87.5k 0

选择分支

Python 没有 switch 分支,所有的多选择分支都使用 if 作为代替。其中,else if 语法被简化成了 elif。如:

identify = "Student"

if identify is "Student":
    print("he is a student.")
elif identify is "Tutor":
    print("he is a tutor")
elif identify is "Professor":
    print("he is a Professor")
else:
    print("unknown.")

Python 的 if 语句还有另外一个用途:充当其它编程语言中的三目运算符。逻辑为:若 if 的表达式成立,则赋前值,否则赋后值。如:

# a = if (10 > 1) ? true : false
# 如果 10 > 1 成立,则 a = True。否则,a = False。
a = True if 10 > 1 else False
print(a)

最值判断

Python 提供了内置的 max()min() 函数简化了查找操作。比如:

seq = [3,6,7,8,1,4,2]
max(seq)
min(seq)

如果内部的元素是对象,则需要额外传入一个表达式规定比较的字段,比如:

class Foo:
    def __init__(self,v_):
        self.v = v_

list = [Foo(1),Foo(2),Foo(3)]
#  规定按照 Foo 的 v 值进行比较。
mx = max(list, key=lambda foo: foo.v)
print(mx.v)

区间判断

若判断某数值变量的值域,Python 提供了可读性更高的写法。如:

x = 100
# other lang: if(x >= 0 && x  0 else (None, ArithmeticError(f"{x} is a invalid value"))

nonNegative = [f(x) for x in xs]
right = map(lambda x: x[0], filter(lambda x: x[0] is not None, nonNegative))
left  = map(lambda x: x[1], filter(lambda x: x[0] is None, nonNegative))

print(*right)  # 输出正常处理之后的数据
print(*left, sep="\n")  # 输出数据处理过程中都遇到了哪些异常

通过这种方式,我们可以将数据流处理的逻辑和异常处理的逻辑相互分离。

with 关键字实现资源开闭

把 with 语法看作是更加抽象的 try - catch 模型。

当涉及到打开 IO 流,或者是加锁这类场景时,自动化关闭资源的手段会帮我们省下很多功夫,就像 Go 语言的 defer 机制。

f = open(filePath, mode="r", encoding="UTF-8")
f.readline()
f.close()

Python 通过 with .. as 关键字提供了 通用的后置通知操作。现在,文件关闭可以改写成下面的逻辑:

with open(filePath, mode="r", encoding="UTF-8") as f:
    f.readline()
    pass

with 语句块在底层借助了 __enter__()__exit__()。换句话说,任何实现了这两个魔法函数的类实例都可以使用 with 语句块。下面是一个简单的例子:

class Daemon:
    def __enter__(self):
        # TODO
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        r = "success" if exc_type is None else "failure"
        print(f"end.{r}")

d = Daemon()
with d:
    print("do something")
    pass

在这段代码中,简单表达式 d 指向一个 Daemon 类实例。内部的代码块将被 __exit__() 函数 守护,无论代码块执行成功与否,该函数总被调用。当语句块内部抛出异常时,exc_typeexc_valexc_tb 三个参数将为非 None 值。

d = Daemon()
with d:
    #  end.failure
    print(1 / 0)
    pass

除此之外,如果 __enter__() 函数返回了有意义的非 None 值,我们可以通过 as 关键字来接收。比如:

class Daemon:
    def __enter__(self):
        return 10, 5

    def __exit__(self, exc_type, exc_val, exc_tb):
        r = "success" if exc_type is None else "failure"
        print(f"end.{r}")

d = Daemon()

# Daemon 的 __enter__() 函数返回 10, 5 两个值,因此这里使用元组提取到 x1, x2 参数。
with d as (x1, x2):
    #  2.0
    #  end.success
    print(x1 / x2)
    pass

不难想到,with 语句块还能用来设计隐蔽的 try ... catch ... finally 逻辑,以此屏蔽掉清除资源的各种细节,这可以提升用户代码的可读性。

函数声明的细节

直接存在于模块的函数定义一般被称之为 function,而定义在类的函数一般称之方法 method。

Python 的函数不严格要求定义返回值类型,但是严格要求使用显式的 return 关键字声明返回值。

def add(a, b): return a + b

一个规范参数与返回值类型的函数可以声明为:

def add(a: int, b: int) -> int: return a + b

这种类型规范只有声明的意味,因为 Python 并不是一门编译型的语言。因此,即使传入了不匹配类型的参数,解释器也不会拒绝执行。

如果一个函数不返回任何有意义的值,那么返回值类型相当于 None。比如:

def println(anything) -> None: print(anything)

在定义函数可以设定参数的默认值。比如:

def f(v1=0, v2=0): return v1 + v2 
print(f()) # 0

函数可以声明 可变参数 表示该参数位置接收任意多个值,参数名前面使用一个 * 号作修饰。比如:

# non-keyword arguments
def receive(*args):
    print(type(args))
    for i in args:
        print(i, end=", ")

receive(1, 2, 3, 4, 5)

# 将列表拆分为可变参数传入
xs = [1, 2, 3, 4, 5]
receive(*xs)

其中,输入的多参数 1, 2, ... 5 被包裹为一个元组 tuple 类型。而如果参数名前面使用两个星号 ** 修饰,则表示它接收的是任意多个键值对。比如:

# keyword arguments
def config(**kwargs):
    print(type(kwargs))
    for k in kwargs:
        print(kwargs[k], end=", ")


config(port=8080, server="tomcat", max_connetion=500)

整个参数列表将会被包裹成一个 dict 字典,这里要求键 key 必须是 str 类型。可以将外部的字典作为可变键值对参数传入函数内。比如:

dictionary = {"a": "abandon", "b": "banana", "c": "clap", "d:": "12"}
conf(**dictionary)

为了避免混淆,Python 规定普通参数,可变参数,可变键值对参数的排列顺序依次为:

def foo(parm,*args,**kwargs):pass

有时为了提高代码的可读性,我们也会选择以 **kwargs 的形式传入参数,这在绝大部分情况都没有什么问题。比如:

def f(v1=0, v2=0): return v1 + v2
print(f(v2=3))  # 3

比如字典的 get() 方法是个例外,见:python - TypeError: get() takes no keyword arguments - Stack Overflow。

一切 C-level 层次的 Python API 都不支持传入 **kwargs

函数内部可以定义函数,且函数自身可以返回另一个函数。比如:

def hof(p1):
    # 函数 f 只在 hof 定义域内部可用。
    def f(p2): return 2 * p2 + p1
    # 函数标识符 f 表示返回 f 本身。
    return f

ff = hof(5)
y = ff(1)
print(y)

这种特性有很多可引申的话题,见后文设计模式中的面向函数编程。

作者:花花子

来源:稀土掘金

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论