返回值(return)

  • 返回值就是函数执行以后返回的结果
  • 用return来指定函数的返回值
  • 我们可以直接使用函数的返回值也可以通过变量来接受函数的返回值
  • return 后面可以跟任意对象,甚至是一个函数
def fn():
    # return 'python' # return可以返回任意对象
    # return (1, 2, 3)
    # return {'a':1, 'b':2}
    return 100
r = fn() # 用变量接受
print(r)    
print(fn()) # 直接打印,不推荐使用此法
# 100
# 100

下面代码中

  • fn2是函数对象,打印fn2实际上是打印函数对象,函数存储在内存中的地址(十六进制)
  • fn2()是调用函数,打印fn(),实际是在调用函数,打印得是函数的返回值
    两种方式都可以实现打印hello的效果,选用fn()时,r接收的是调用函数fn2(),打印r的结果就是hello
def fn():
    def fn2():
        print('hello')
    return fn2
r = fn() # 用r接收函数fn2对象
print(r) 
r()  # 调用函数
# <function fn.<locals>.fn2 at 0x0000015B8A6E55E8>
# hello

若只写一个return,而没有返回值,会返回None

def fn():
    return
r = fn()
print(r)
# None

在函数中return后面的代码不会被执行,一旦return执行就会自动结束函数

def fn():
    print('我执行了')
    return
    print('我睡着了')
r = fn()    
print(r)
# 我执行了
# None

文档字符串

help()函数可以查询其他函数的用法
help(print)
# print(...)
    # print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

    # Prints the values to a stream, or to sys.stdout by default.
    # Optional keyword arguments:
    # file:  a file-like object (stream); defaults to the current sys.stdout.
    # sep:   string inserted between values, default a space.
    # end:   string appended after the last value, default a newline.
    # flush: whether to forcibly flush the stream.

为了提高代码的可读性,我们通常会对代码注释,解释说明该断代码
我写这篇博客时用的是vscode,可能是我没有安装这块儿插件吧,没有这个功能。pycharm中,在函数内输入三个引号‘’‘然后回车,会自动生成参数的说明,后面的备注需要自己写,这里为了举例子,随便写的。
下面fn(a:int, b:str, c:boll)参数后面的东西,不会有任何作用,知识起到注释的作用,但不建议这么写,这样容易出现错误。建议使用函数内部的注释方式。
->int的意思是返回的是一个整形

def fn(a:int,b:str,c:bool)-> int:

    '''
    :param a: 类型 int 默认值...
    :param b: 类型 str 默认值...
    :param c: 类型 bool 默认值...
    :return: int
    '''

    return 1

help(fn)
r = fn(5, 2, 3)
print(r)
# Help on function fn in module __main__:                                                           

# fn(a: int, b: str, c: bool) -> int
#     :param a: 类型 int 默认值...
#     :param b: 类型 str 默认值...
#     :param c: 类型 bool 默认值...
#     :return: int

# 1

## 函数的作用域

作用域指的就是变量生效的区域
全局作用域

  • 全局作用域在程序执行时创建,在程序执行结束时销毁
  • 所有函数意外的部分都是全局作用域
  • 在全局作用中定义的变量,都属于全局变量,全局变量可以在程序的任意位置访问

函数作用域

  • 函数作用域在函数调用时创建,在调用结束后销毁
  • 函数每调用一次会产生一个新的作用域
  • 在函数作用于中定义的变量都是局部变量,它只能在函数内部被访问到
a = 'i am global scope'
def fn():
    b = 'i am funcation scope'
    print('局部', b)
fn()    
print('全局', a)
# 局部 i am funcation scope
# 全局 i am global scope

全局变量可以传入内部

a = 10
def fn():
    print(a)
fn()
# 10

但是函数变量不能在外部调用,函数外部是访问不到的
同时函数内部修改变量,不会影响到外部,且返回的结果已内部为准

a = 10
def fn():
    a = 20
    b = 10
    def fn2():
        a = 30
        print(a)
    fn2()
fn()
# print(b) 
# 30
# print(b)---NameError: name 'b' is not defined

若想通过函数内部改变全局变量需要用到global

a = 10
def fn():
    global a
    a = 20
    print('我是局部变量a', a)
fn()
print('我是全局变量a', a)
# 我是局部变量a 20
# 我是全局变量a 20

命名空间

命名空间实际上就是一个字典,是一个专门用来存储变量的字典
locals()

  • 用来获取当前作用域的命名空间
  • 如果在全局作用域中调用locals()则获取的是全局命名空间
  • 如果在函数作用域中调用locals()则获取的是函数命名空间
  • 调用locals(),返回的是一个字典
a = 10
def fn():
    a = 20
    b = 10
    def fn2():
        a = 30
        print(a)
    fn2()
fn()
s = locals()
s['b'] = 110 # 命名空间是一个字典,我们可以向正常字典一样,向里面添加键值对,但不推荐这么做,当你这么做的时候,别人可能就要打110了
print(s)
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002DE2D25CD88>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'f:/my_dream/text.py', '__cached__': None, 
# 'a': 10, 'fn': <function fn at 0x000002DE2F3D5048>, 's': {...}, 'b': 110}

在内部函数中调用

a = 10
def fn():
    b = 20
    s = locals()
    print(s)
fn()   
# {'b': 20}

递归

  • 递归简单的理解就是自己调用自己
  • 递归式函数,在函数中调用自己

递归的条件

  1. 基线条件
  • 问题可以被分解为最小的问题(即不再分解),但满足基线条件时,不再递归
  1. 递归条件
  • 将问题可以分解的条件

递归的练习

通过练习去学习递归,感受递归,下面的练习你可能会觉得问题复杂化了,当然解决问题的方法有很多,我想表达的是递归的思想

  1. 实现任意数的阶乘
  • 阶乘用’!‘表示,如5! = 12345
    我们想要实现一个小算法,首先先看看10!
    10!= 12345678910 = 109!
    9!= 123456789 = 98!

    2!= 1
    2 = 2 * 1!
    1!= 1 = 1
def fn(n):
    # 基线条件,当n == 1时无法再分解
    if n == 1:
        return 1
    # 递归条件    
    return n * fn(n-1)
r = fn(6)
print(r)
# 720
  1. 创建一个函数来为任意数做幂运算 如 ni 54
    首先举个例子,看看65
    6
    5 = 66666 = 6 * 6 ** 4
    64 = 666*6 = 6 * 6 ** 3
    6
    3 = 666 = 6 * 6 ** 2
    6**2 = 6*6 = 6 * 6 ** 1
    6 = 6 = 6
def fn(n, i):
    if i == 1:
        return n
    elif n == 1:
        return 1
    return n * fn(n, i-1)
power = fn(6, 5)
print(power)
print(6**5)
# 7776
# 7776
  1. 创建一个函数,用来检测任意字符串是否是回文字符串,如果是返回True,如果不是返回False
    什么是回文字符串?
    字符串从后往前读和从后往前读是一样的 例如 abcba abc
    例如shellxllehs
    我们先看最左和最右的是不是一致
    shellxllehs
    hellxlleh
    ellxlle

    x
    下面提供两段代码,第一段中,如果不判断elif len(s) != 1,会报错
    因为判断方式的不合理,造成了代码冗长,但是这是我做这个题目的第一想法,该方法仅做反面教材
def fn(s):
    if len(s) == 1:
        return True
    elif len(s) != 1:
        pass
    elif s[0] != s[-1]:
        return False
    return fn(s[1:-1])
r = fn('shellxllehs')
print(r)
# True

比较合理的方法,如下

def fn(s):
    if len(s) < 2:
        return True
    elif s[0] != s[-1]:
        return False
    return fn(s[1:-1])
r = fn('shellxllehs')
print(r)
# True