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

2022-09-1811:35:39编程语言入门到精通Comments682 views字数 4014阅读模式

选择分支

Python 没有 switch 分支,所有的多选择分支都使用 if 作为代替。其中,else if 语法被简化成了 elif。如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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.")

Pythonif 语句还有另外一个用途:充当其它编程语言中的三目运算符。逻辑为:若 if 的表达式成立,则赋前值,否则赋后值。如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

最值判断

Python 提供了内置的 max()min() 函数简化了查找操作。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

如果内部的元素是对象,则需要额外传入一个表达式规定比较的字段,比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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 提供了可读性更高的写法。如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

x = 100
# other lang: if(x >= 0 && x <= 100){...}
if 0 <= x <= 100:
    print("x in [0,100]")
else:
    print("x not in [0,100]")

断言

断言是一种严格的条件判断。使用 assert 关键字创建一个断言,并附带由条件式 condmsg 组成的二元组。当条件式判别为 False 时,程序会抛出一个 AssertionError 异常,并将 msg 消息输出到控制台。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

x = 100
y = 0

assert y != 0, "y should not be 0."
# 下方的代码是不可达的。
z = x / y

异常捕获

Python 使用 tryexceptfinally ( 可缺省 ) 来守护一段代码块,并在代码块抛出异常时捕捉,避免程序中断退出。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

try:
    100 / 0
except ZeroDivisionError as e: # 将捕获到的异常赋值给 e
    print(f"error! => {e}")
finally: # finally 是可选的
    print("done.")

如果要捕获多个异常,可以将 except 写成:exception (ErrorType1, ErrorType2, ...) as e:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

可以通过 raise 关键字主动抛出异常。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

raise Exception("throw a new Exception by user code")

在函数式风格的数据流处理中,函数会一般会将异常值收集起来,并统一抛给上层代码。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

def f(x: int): return (x, None) if 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")  # 输出数据处理过程中都遇到了哪些异常

通过这种方式,我们可以将数据流处理的逻辑和异常处理的逻辑相互分离。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

with 关键字实现资源开闭

把 with 语法看作是更加抽象的 try - catch 模型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

当涉及到打开 IO 流,或者是加锁这类场景时,自动化关闭资源的手段会帮我们省下很多功夫,就像 Go 语言的 defer 机制。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

Python 通过 with .. as 关键字提供了 通用的后置通知操作。现在,文件关闭可以改写成下面的逻辑:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

with 语句块在底层借助了 __enter__()__exit__()。换句话说,任何实现了这两个魔法函数的类实例都可以使用 with 语句块。下面是一个简单的例子:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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 值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

除此之外,如果 __enter__() 函数返回了有意义的非 None 值,我们可以通过 as 关键字来接收。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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 逻辑,以此屏蔽掉清除资源的各种细节,这可以提升用户代码的可读性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

函数声明的细节

直接存在于模块的函数定义一般被称之为 function,而定义在类的函数一般称之方法 method。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

Python 的函数不严格要求定义返回值类型,但是严格要求使用显式的 return 关键字声明返回值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

一个规范参数与返回值类型的函数可以声明为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

这种类型规范只有声明的意味,因为 Python 并不是一门编译型的语言。因此,即使传入了不匹配类型的参数,解释器也不会拒绝执行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

如果一个函数不返回任何有意义的值,那么返回值类型相当于 None。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

在定义函数可以设定参数的默认值。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

函数可以声明 可变参数 表示该参数位置接收任意多个值,参数名前面使用一个 * 号作修饰。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

# 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 类型。而如果参数名前面使用两个星号 ** 修饰,则表示它接收的是任意多个键值对。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

# 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 类型。可以将外部的字典作为可变键值对参数传入函数内。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

为了避免混淆,Python 规定普通参数,可变参数,可变键值对参数的排列顺序依次为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

有时为了提高代码的可读性,我们也会选择以 **kwargs 的形式传入参数,这在绝大部分情况都没有什么问题。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

比如字典的 get() 方法是个例外,见:python - TypeError: get() takes no keyword arguments - Stack Overflow文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

一切 C-level 层次的 Python API 都不支持传入 **kwargs文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

函数内部可以定义函数,且函数自身可以返回另一个函数。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

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

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

这种特性有很多可引申的话题,见后文设计模式中的面向函数编程。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

作者:花花子文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

来源:稀土掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27838.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/27838.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定