python之基本逻辑(函数)语句

python基本逻辑(函数)语句

author:Once Day date:2022年2月18日

本文档在于总结相关内容,零散的知识难以记忆学习。

本文档基于windows平台。

全系列文档查看:python基础_CSDN博客

1.基本规则

Python代码在执行过程中,遵循下面的基本原则:

  • 普通语句,直接执行;
  • 碰到函数,将函数体载入内存,并不直接执行
  • 碰到类,执行类内部的普通语句,但是类的方法只载入,不执行
  • 碰到if、for等控制语句,按相应控制流程执行
  • 碰到@,break,continue等,按规定语法执行
  • 碰到函数、方法调用等,转而执行函数内部代码,执行完毕继续执行原有顺序代码

这里推荐阅读参考文档:python 顺序执行 - 刘江的python教程 (liujiangblog.com)

2. if __name__ == '__main__'

__name__是所有模块的内置属性:

  • 导入的时候,默认值为文件名,即__name__=filename
  • 直接运行时__name__=__main__

很明显,可以用这个值来判断导入和运行两种情况!

3.条件判断

Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。

3.1 if /elif/else
if condition1:
	statement1
elif condition2:
    statement2
else:
    statement3

基本结构如上,但可以拓展以及镶嵌。

编程新手推荐阅读以下文档:

3.2 match-case(python3.10)

在其他语言(比如说经典的C语言)中有一种多分支条件判断语句,即switch。

具体描述请参考其他语言选择结构,这里只上例子!

match variable: #这里的variable是需要判断的内容
    case ["quit"]: 
        statement_block_1 # 对应案例的执行代码,当variable="quit"时执行statement_block_1
    case ["go", direction]: 
        statement_block_2
    case ["drop", *objects]: 
        statement_block_3
    ... # 其他的case语句
    case _: #如果上面的case语句没有命中,则执行这个代码块,类似于Switch的default
        statement_block_default

推荐阅读:PEP 622 – Structural Pattern Matching | Python.org

4.循环while、for

直接上例子,编程新手可以参考以下文档:

直接while和for循环:

while 判断条件:
    statements
    
for <variable> in <sequence>:
  <statements>
else:
  <statements>

循环后接else:

for/while xxx:
	xxxxxx
else:
	xxxxx

循环语句可以有 else 子句;它在穷尽列表(for 循环)或条件变为假(while 循环)循环终止时被执行。

当循环被break终止时,不会执行else中的语句。

5.中断操作break,continue

注意:Python的break只能退出一层循环

break用于中断循环,即直接结束循环。

continue用于结束当前轮循环。

编程新手推荐阅读下列文档:python 循环控制 - 刘江的python教程 (liujiangblog.com)

例子:

for:
	break/continue;

6.pass语句

pass 语句什么都不做。它只在语法上需要一条语句但程序不需要任何操作时使用。

>>> while True:
...     pass  # 等待键盘中断 (Ctrl+C)
>>> class MyEmptyClass:
...     pass

7.Python函数

编程新手推荐阅读:

本文章是总结性质,所以这些编程语言通用概念的地方会概括性描述!以节约时间精力.

7.1 函数定义
def 函数名(参数):
     """
    这里是函数的说明文档,doc的位置
    :param lis: 参数列表的说明
    :return: 返回值的说明
    """
    # 内部代码
    return 表达式 

注意:

  • 函数的第一行语句后可以选择性地使用文档字符串—用于存放函数说明。
  • 使用return结束函数。默认返回None。可以是表达式及任意对象。

注意:return是可以放在判断语句中用以快速结束函数!当然后面代码会被忽略!

def func():
	return 1,2,3,....
	
r1,r2,r3,...=func()

注意return可以返回多个值

7.2 函数参数类型

编程新手强烈建议阅读:python 参数类型 - 刘江的python教程 (liujiangblog.com)

下面的描述是基于对c和c++语言已经熟悉且有一定的编程经历

需要注意Python的函数参数传递的是实际对象的内存地址。

对于可变类型,相当于C语言传递指针进去!所以可修改外部变量的值。

对于不可变类型,等同于C语言传递值进去,无法影响外部变量的值。

  • 位置形参数,要求形参和实参一一对应。

    def add(a,b,c):
    	xxxxxx
    add(1,2,3)#a=1,b=2,c=3
    
  • 默认参数,当不提供实参时,用默认参数代替。默认参数需要在位置参数后面

    def student(name, sex, age, classroom="101", tel="88880000", address="..."):
        pass
    student('mary','female',18,tel='666666','beijing')   #  这是错误的参数传递方式
    student("mary","female",18,tel="666666",address="beijing")
    

    注意一切没有提供参数名的实际参数,都会当做位置参数按顺序从参数列表的左边开头往右匹配!

  • 使用参数名传递参数时,可以不按顺序填入。

  • 默认参数尽量指向不变的对象!,这个机制挺奇怪的!

  • 动态参数。Python的动态参数有两种,分别是*args**kwargs,名字可以任取。

    注意:动态参数,必须放在所有的位置参数和默认参数后面!

    def func(name, age, sex='male', *args, **kwargs):
        pass
    

    *args一个星号表示接收任意个参数。调用时,会将实际参数打包成一个元组传入形式参数。如果参数是个列表,会将整个列表当做一个参数传入。

    def func(*args):
        xxxxxx
    
    li = [1, 2, 3]
    func(li)#这样调用列表被当成整体传入
    func(*li)#这样序列对象会将内部元素逐一作为参数传入进去。字典是把key传进去。
    

    *kwargs两个星表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典。

    def func(**kwargs):
    	xxxxxx
        
    func(k1='v1', k2=[0, 1, 2])#类似指定参数名的参数传递,但是会被收集起来,形式key=value
    dic = {
        'k1': 'v1',
        'k2': 'v2'
    }
    func(**dic)#需要双引号将字典键值对逐一传递过去。
    

    *args**kwargs组合起来使用,理论上能接受任何形式和任意数量的参数,*args必须出现在**kwargs之前.

    注意,*和**只是把序列和字典转变成位置参数和关键字参数,然后再进行参数收集。

    所以还是要遵守位置参数和使用参数名传递参数的规则。

  • 关键字参数,关键字参数前面需要一个特殊分隔符*和位置参数及默认参数分隔开来,*后面的参数被视为关键字参数。关键字参数必须传入参数名,不然将报错!

    def student(name, age, *, sex):
        pass
    

    有了一个*args参数,后面就不再需要分隔符*了。

    def student(name, age=10, *args, sex, classroom, **kwargs):
        pass
    
7.3 (函数)变量作用域

作用域指的是变量的有效范围

编程新手建议阅读:python 变量作用域 - 刘江的python教程 (liujiangblog.com)

注意下面的描述是基于对c和c++中变量作用域已经熟悉的情况!

  • python中没有块级作用域,if/for/while等语句内部变量对外部可见。但建议采取C语言先定义再使用的风格!

    if True:
    	x=145
    print(x)
    #x=145  不存在块级作用域
    #推荐以下编程风格
    x=0
    if True:
    	x=145
    print(x)
    
  • python有四层作用域

    1. local局部作用域
    2. Enclosing 闭包函数外的函数作用域
    3. Global全局作用域
    4. Built-in内建作用域

    查找顺序:Local->Enclosing->Global->Built-in

  • 局部变量是相对的,可能被更小范围内的函数范围。

  • 在函数内部修改外面的全局变量,要使用关键字global。注意这和访问不同!访问外部变量,无需声明也可以实现访问。

  • 使用nonlocal关键字!它可以修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量。

  • 任何函数内部要被修改的变量,如果没有使用关键字,则默认为局部变量。

  • Python函数的作用域取决于其函数代码块在整体代码中的位置,而不是调用时机的位置

7.4 递归函数

递归函数最好的用处就是简单化编程!

编程新手应阅读数学递归的思想,以及阅读以下文章:

递归函数的优点是定义简单,代码量少,逻辑清晰。与递归相对的是迭代(循环等),绝大多数递归问题也能用迭代法解决,但是逻辑上不如递归明确。

注意极少数问题是无法用迭代和递归解决的,因为时间及空间复杂度增长过快.(而递归主要是空间复杂度高)。

对于递归,需要注意递归深度,在Python中,通常情况下,这个深度是1000层,超过将抛出异常。

7.5 匿名函数

推荐直接阅读:python 匿名函数 - 刘江的python教程 (liujiangblog.com)

以下只是从上面文章的摘抄总结

创建函数时,有些时候,不需要显式地定义函数,可省略命名。

Python语言使用lambda关键字来创建匿名函数。

所谓匿名,即不再使用def语句这样标准的形式定义一个函数。

  • lambda只是一个表达式,而不是一个代码块,函数体比def简单很多。
  • 仅仅能在lambda表达式中封装有限的逻辑。
  • lambda 函数拥有自己的命名空间。

其形式通常是这样的:lambda 参数: 表达式

例如:lambda x: x * x。它相当于下面的函数:

def f(x):
    return x * x

关键字lambda表示匿名函数,冒号前面的x表示函数参数,x*x是执行代码。

匿名函数只能有一个表达式,不用也不能写return语句,表达式的结果就是其返回值。

匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

8.Python推导式

详细阅读推荐以下文章:

注意以下内容只是从上面文章的摘抄总结

8.1 列表推导式

一种快速生成列表的方式。其形式是用方括号括起来的一段语句。

>>> [x * x for x in [1,2,3,4,5,6]]
[1, 4, 9, 16, 25, 36]

很明显,遍历列表,把值代入表达式计算,结果排列成列表。

更直接理解,直接把表达式在列表的每个元素中计算一遍!

使用条件语句:

>>> [x * x for x in [1,2,3,4,5,6] if x%3 ==0 ]
[9, 36]

使用嵌套循环:

>>> [x * y for x in [1,2,3,4,5,6] for y in [1,10]]
[1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60]

遍历+解包数据

>>> [x + y for x,y in [(1,10),[2,3],(4,5),'sh',['once','day']]]
[11, 5, 9, 'sh', 'onceday']

**只给出x和y,所以包里只能有两个元素!**否则将报错!

>>> [x + y for x,y in [(1,10),[2,3],(4,5),'shd',['once','day']]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
ValueError: too many values to unpack (expected 2)
8.2 字典推导式

快速生成字典。

>>> {x: x+x for x in [2,'onceday',45]}
{2: 4, 'onceday': 'oncedayonceday', 45: 90}
8.3 集合推导式

注意没有冒号:

>>> {x+x for x in [2,'onceday',45]}
{'oncedayonceday', 90, 4}

9.迭代器

迭代:循环遍历对象的每一个元素的过程。

在Python中,list/tuple/string/dict/set/bytes都是可以迭代的数据类型。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。迭代器通常要实现两个基本的方法:iter()next()

>>> l='onceday'
>>> it=iter(l)
>>> it
<str_iterator object at 0x000002C1507B93D0>
>>> next(it)
'o'
>>> next(it)
'n'

实现迭代器类需要实现__iter__()__next__()方法。

Python的for循环本质上就是通过不断调用next()函数实现的。

迭代器对象可以使用常规 for 语句进行遍历。

推荐阅读:

10.生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次执行 next() 方法时从当前位置继续运行。

def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成

通过圆括号可以编写生成器推导式:

>>> g=(x * x for x in [1,2,3,4])
>>> g
<generator object <genexpr> at 0x000002C150C0CC80>

生成式的值应该通过next函数来访问!但是当最后一个元素被访问后,再次访问就会抛出错误。

使用for遍历则不会抛出错误!

推荐阅读:

11.装饰器

装饰器(Decorator):从字面上理解,就是装饰对象的器件。可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出。

装饰器的语法是将@装饰器名,放在被装饰对象上面。

@dec
def func():
    pass

注意编程新手需要对函数有深入的了解!推荐直接阅读文章:

11.1 基础装饰器

装饰器的语法规则是:被装饰的函数的名字会被当作参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数。

注意程序第一次读入@dev时也会执行一遍内部代码,应该是初始化全部变量(代码)部分!

def outer(func):
	#全局部分,被装饰函数初始化会执行一遍
	print("初始化装饰函数")
	def inner():
 		print("准备使用函数!")
        result = func()
        print("使用函数结束")
        return result
 	return inner

@outer
def f1():
  	print("调用函数f1......")

@outer
def f2():
  	print("调用函数f2......")
    
#输出结果
初始化装饰函数
初始化装饰函数


f1()
f2()
#输出结果
准备使用函数!
调用函数f1......
使用函数结束
准备使用函数!
调用函数f2......
使用函数结束

可以用*args和**kwargs传进任意参数:

def outer(func):
    #全局部分,被装饰函数初始化会执行一遍
    print("初始化装饰函数")
    def inner(name='none',age=None):
        print("准备使用函数!")
        result = func(name,age)
        print("使用函数结束")
        return result
    return inner

@outer
def f1(name,age=18):
    print("调用函数f1......")
    print(name)
    print(age)

@outer
def f2():
    print("调用函数f2......")

f1('onceday')
f2()
#输出结果
初始化装饰函数
初始化装饰函数
准备使用函数!
调用函数f1......
onceday
None
使用函数结束
准备使用函数!
TypeError: f2() takes 0 positional arguments but 2 were given

可以看到,传统的固定参数形式,无法适配不同函数传递参数的多样性!

def outer(func):
    #全局部分,被装饰函数初始化会执行一遍
    print("初始化装饰函数")
    def inner(*args,**kwargs):
        print("准备使用函数!")
        result = func(*args,**kwargs)
        print("使用函数结束")
        return result
    return inner

@outer
def f1(name,age=18):
    print("调用函数f1......")
    print(name)
    print(age)

@outer
def f2():
    print("调用函数f2......")

f1('onceday')
f2()
#输出结果
初始化装饰函数
初始化装饰函数
准备使用函数!
调用函数f1......
onceday
18
使用函数结束
准备使用函数!
调用函数f2......
使用函数结束

可以看到,输出结果符合预期

11.2 多重装饰器

可以使用多个装饰器修饰一个函数

def outer(func):
    # 全局部分,被装饰函数初始化会执行一遍
    print("初始化装饰函数")

    def inner(*args, **kwargs):
        print("准备使用函数!")
        result = func(*args, **kwargs)
        print("使用函数结束")
        return result

    return inner


def outer2(func):
    # 全局部分,被装饰函数初始化会执行一遍
    print("初始化装饰函数2")

    def inner(*args, **kwargs):
        print("准备使用函数!2")
        result = func(*args, **kwargs)
        print("使用函数结束2")
        return result

    return inner

@outer
@outer2
def f1(name, age=18):
    print("调用函数f1......")
    print(name)
    print(age)

@outer
def f2():
    print("调用函数f2......")

f1('onceday')
#输出结果
初始化装饰函数2
初始化装饰函数
初始化装饰函数#可以看到内圈的先被调用
准备使用函数!
准备使用函数!2
调用函数f1......
onceday
18
使用函数结束2
使用函数结束
11.3 装饰器函数函数

从以下代码不难看出,装饰器内部代码是逐级深入的,而且运行的时间也不一样,外层在初始化时使用,内层在调用时运行!

def outer(test,func_id):
    # 全局部分,被装饰函数初始化会执行一遍
    print("外层初始化装饰函数")
    print(test)
    print(func_id)
    def midd(main_func):
        # 全局部分,被装饰函数初始化会执行一遍
        print("中层初始化装饰函数")
        def inner(*args, **kwargs):
            test(*args,**kwargs)
            print("准备使用函数!"+str(func_id))
            result = main_func(*args, **kwargs)
            print("使用函数结束")
            return result

        return inner
    return midd

def funt(name='none'):
    print("是这个名字吗:")
    print(name)

@outer(funt,1)
def f1(name, age=18):
    print("调用函数f1......")
    print(name)
    print(age)

@outer(funt,2)
def f2():
    print("调用函数f2......")

print('***********')
f1('onceday')
print('***********')
f2()
print('***********')

#输出结果
外层初始化装饰函数
<function funt at 0x000001EC27D7C700>
1
中层初始化装饰函数
外层初始化装饰函数
<function funt at 0x000001EC27D7C700>
2
中层初始化装饰函数
***********
是这个名字吗:
onceday
准备使用函数!1
调用函数f1......
onceday
18
使用函数结束
***********
是这个名字吗:
none
准备使用函数!2
调用函数f2......
使用函数结束
***********

13.基本数据结构

这部分不介绍,只推荐文档,仅供新手阅读:

注:本文章内容收集整理于互联网,仅供学习之用!


版权声明:本文为Once_day原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。