引子 格式化字符串f-string

简介:
f-string在形式上是以 f 或 F 修饰符引领的字符串(f’xxx’ 或 F’xxx’),以大括号 {} 标明被替换的字段;f-string在本质上并不是字符串常量,而是一个在运行时运算求值的表达式。
使用:

#实例1 f-string用大括号 {} 表示被替换字段,其中直接填入替换内容
name = 'Eric'
print(f'Hello, my name is {name}') #Hello, my name is Eric

number = 7
print(f'My lucky number is {number}')#My lucky number is 7

#实例2 f-string的大括号 {} 可以填入表达式或调用函数,Python会求出其结果并填入返回的字符串内
print(f'A total number of {24 * 8 + 4}')#'A total number of 196'

name = 'ERIC'
print(f'My name is {name.lower()}')#'My name is eric'

import math
print(f'The answer is {math.log(math.pi)}')#'The answer is 1.1447298858494002'

#实例3 大括号外的引号还可以使用 \ 转义,但大括号内不能使用 \ 转义:
print( f'''He\'ll say {"I'm Eric"}''') #He'll say I'm Eric

print( f'''He'll say {"I\'m Eric"}''')#出错

#实例4 f-string大括号外如果需要显示大括号,则应输入连续两个大括号 {{ 和 }}:
print(f'{{5}} {"stars"}') #{5} stars

#实例5 -string大括号内根本就不允许出现 \。如果确实需要 \,
#则应首先将包含 \ 的内容用一个变量表示,再在f-string大括号内填入变量名:

print(f"newline: {ord('\n')}") #出错

newline = ord('\n')
print(f"newline: {newline}") #10

#f-string大括号内也可填入lambda表达式,但lambda表达式的 : 会被f-string误认为是表达式与格式描述符之间的分隔#符,为避免歧义,需要将lambda表达式置于括号 () 内:

函数

定义函数

自定义函数遵循的规则:

  1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
  2. 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  4. 函数内容以冒号起始,并且缩进。
  5. return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

参数传递

在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

  • 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。
  • 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python 函数的参数传递:

  • 不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
  • 可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响

python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

函数参数

位置参数(也称必备参数)

必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

#实例1 注意参数位置
def myfunc(o,v,c):
    return " ".join((c,v,o))

print(myfunc("我","吃","鱼"))#鱼 吃 我

关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。

使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

def myfunc(o,v,c):
    return " ".join((c,v,o))

print(myfunc(c="我",v="吃",o="鱼"))#我 吃 鱼

位置参数必须在关键字参数之前
函数参数中有/ 那么/左侧只能是位置参数,关键字参数会报错
函数参数中有* 那么*右侧只能是关键字参数,位置参数会报错

默认参数

调用函数时,默认参数的值如果没有传入,则被认为是默认值。
如果使用默认参数,就应该将默认参数摆到最后

def myfunc(name,age=18):
    print("Name:",name)
    print("Age:",age)
    return

myfunc("Nikki")#age为默认值18
myfunc("Lan",20)#传入的20覆盖默认值18

不定长参数(又称收集参数)

能处理比当初声明时更多的参数。这些参数叫做不定长参数。
不定长参数可以将多个参数打包到一个元组里头,做法是使用*打包,
收集参数还可以将关键字参数的形式转换为字典,做法是使用**打包
基本语法:

def functionname([formal_args,] *var_args_tuple ):
   "函数_文档字符串"
   function_suite
   return [expression]

实例1 打包成元组

def printinfo(arg1, *vartuple):
    "打印任何传入的参数"
    print("输出: ")
    print(vartuple) #vartuple以元组的形式存储
    for var in vartuple:
        print(var)
    return vartuple
# 调用printinfo 函数
printinfo(10)
printinfo(70, 60, 50,40)

输出结果:

输出: 
()
输出: 
(60, 50, 40)
60
50
40

实例2 打包成字典

def myfunc(**kwargs):
    print(kwargs)

myfunc(a=1,b=2,c=3)

#输出为 {'a': 1, 'b': 2, 'c': 3}

如果在收集参数(* 打包元组,打包字典的会出错)后面还需要指定其他参数,那么在调用函数时应该使用关键字参数来指定后面的参数

实例3 知识点融合

def myfunc(a,*b,**c):
    print(a)
    print(b)
    print(c)
myfunc(1,10,20,30,m=2,n=3,d=50)

输出结果:

1
(10, 20, 30)
{'m': 2, 'n': 3, 'd': 50}

解包参数

def myfunc(a,b,c,d):
    print(a,b,c,d)

args=(1,2,3,4)
kwargs={'a':10,'b':20,'c':30,'d':40}
myfunc(*args) #1 2 3 4
myfunc(**kwargs)#10 20 30 40

作用域

局部变量:模块内、所有函数外、所有class外
全局变量:函数内、class的方法(类方法、静态方法、实例方法)内,且变量前面没有修饰

#实例1 区分局部变量和全局变量
x=10 #全局变量
def myfunc():
    x=30#局部变量
    print(f'局部变量x={x}')
myfunc()
print(f'全局变量x={x}')

输出结果:

局部变量x=30
全局变量x=10

golbal:

  1. global在python中叫关键字,不叫命令
  2. global的作用就是引用全局变量到局部作用域中来
  3. 如果全局变量中没有一个变量a,而函数中写了 globa a这样的语句,那么等于在全局变量中创建了一个变量a
#实例2
x=10 #全局变量
def myfunc():
    global x
    x=30
    print(f'global声明的变量x={x}')
myfunc()
print(f'全局变量x={x}')

输出结果:

global声明的变量x=30
全局变量x=30

函数嵌套

闭包

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
闭包中是不能修改外部作用域的局部变量的,实例:

def foo():
    m = 0
    def foo1():
        m=1
        print(m)
    print(m)
    foo1()
    print(m)
foo()

输出结果:

0
1
0

非要在内部函数去修改外部函数的变量 用nonlocal语句:

def foo():
    m = 0
    def foo1():
        nonlocal m
        m=1
        print(m)
    print(m)
    foo1()
    print(m)
foo()

输出结果:

0
1
1

闭包用途

用途1:当闭包执行完后,仍然能够保持住当前的运行环境。比如棋盘游戏的移动

origin = [0, 0]  # 坐标系统原点
legal_x = [0, 50]  # x轴方向的合法坐标
legal_y = [0, 50]  # y轴方向的合法坐标


def create(pos=origin):
    def player(direction, step):
        # 这里应该首先判断参数direction,step的合法性,比如direction不能斜着走,step不能为负等
        # 然后还要对新生成的x,y坐标的合法性进行判断处理,这里主要是想介绍闭包,就不详细写了。
        new_x = pos[0] + direction[0] * step
        new_y = pos[1] + direction[1] * step
        pos[0] = new_x
        pos[1] = new_y
        # 注意!此处不能写成 pos = [new_x, new_y]
        return pos
    return player
player = create()  # 创建棋子player,起点为原点
print(player([1, 0], 10))  # 向x轴正方向移动10步
print(player([0, 1], 20))# 向y轴正方向移动20步
print(player([-1, 0], 10))# 向x轴负方向移动10步

输出结果:

[10, 0]
[10, 20]
[0, 20]

用途2:闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。

x=2
y=3
def power(exp):
    def exp_of(base):
        return base**exp
    return  exp_of
square=power(2)
cube=power(3)
print(f'边长为{x}的正方形面积为:{square(x)}')
print(f'边长为{y}的正方形体积为:{cube(x)}')

输出结果:

边长为2的正方形面积为:4
边长为3的正方形体积为:8

LEGB规则

Python在查找“名称”的时候,是按照LEGB规则查找的
Local Enclosed Global Build-in
局部作用域 嵌套函数的外层函数作用域 全局作用域 内置作用域

装饰器

python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

#实例1 不引用装饰器,倒数第二行就相当于装饰器的作用
import time
def time_master(func):
    def call_fun():
        print("开始运行程序,,,")
        start=time.time()
        func()
        stop=time.time()
        print("结束程序运行,,,")
        print(f'一共耗费了{(stop-start):.2f}秒')
    return call_fun

def myfunc():
    time.sleep(2)
    print("Hello World")

myfunc =time_master(myfunc)#相当于myfun=call_fun
myfunc()
#实例2 引用装饰器
import time
def time_master(func):
    def call_fun():
        print("开始运行程序,,,")
        start=time.time()
        func()
        stop=time.time()
        print("结束程序运行,,,")
        print(f'一共耗费了{(stop-start):.2f}秒')
    return call_fun
@time_master
def myfunc():
    time.sleep(2)
    print("Hello World")
myfunc()

实例1,2的输出结果:

开始运行程序,,,
Hello World
结束程序运行,,,
一共耗费了2.00秒

多个装饰器调用顺序

def dec1(func):
    print("1111")
    def one():
        print("2222")
        func()
        print("3333")
    return one
def dec2(func):
    print("aaaa")
    def two():
        print("bbbb")
        func()
        print("cccc")
    return two
@dec1
@dec2
def test():
    print("test test")
test()

输出结果:

aaaa
1111
2222
bbbb
test test
cccc
3333

解析:15~18行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one。执行test函数就是执行one函数,此时one函数内的func为two,输出结果为2222–>bbbb->test tset->cccc->3333

Lambda表达式(匿名函数)

  • 概念:是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。
  • 原因:函数名并没有实际的意义,为了简便,所以推出了lambada表达式的语法糖!
  • 注意:与其它语言不同,Python的Lambda表达式的函数体只能有单独的一条语句,也就是返回值表达式语句!
  • 理解:匿名函数的等价形式来理解lambada表达式!
  • 注:虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。

语法:
<函数对象名> = lambda <形式参数列表>:<表达式>
作用:

  1. 不用命名,直接实现简单功能;但是如果只是"单个调用"的话,还是要命名变量
  2. 让代码更加简洁
    实例:
from functools import reduce

def add(x, y) :            # 两数相加
    return x + y
sum1 = reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
sum2 = reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
print(sum1) #15
print(sum2) #15
#阶乘
print(list(map(lambda x:x**2,range(5)))) #[0, 1, 4, 9, 16]
#偶数
print(list(filter(lambda x:x%2==0,range(10)))) #[0, 2, 4, 6, 8]
# 需求:求阶乘
f = lambda func, n: 1 if n == 0 else n * func(func, n - 1)
print(f(f, 0))  # 24
#无参
t = lambda: True
print(t()) #True