第六章 Python 函数(二)

第六章 Python 函数(二)

一、一等公民:函数

 在 Python 中对象的优先等级:

      函数 ---> 类 ---> 模块 ---> 包

1. 函数即"变量"

    在这里有必要强调一点:在 Python 中一切皆对象的概念;包括数字、字符串、元组、列表、字典和函数

    对就是,函数也是对象,还有列表、字典和元组中的一个元素都是对象。

    也就是,当你去定义一个对象是,每个对象都是有他的内存地址的

>>> a = 'hlep'
>>> id(a)
140122643069728
>>> for i in a:
...     id(i)
... 
140122643280984
140122643550816
140122643550480
140122643175720
# 现在定义一个函数
>>> def f1():
...     print('只有调用我时,此处才会被执行')
... 
# 查看它的内存地址
>>> id(f1)
140122643775000
>>>

   既然函数也是和其他的对象一样,那么它本身也应该具有某些对象的属性和特点。比如 定义一个列表:

# 这里list1 是一个变量变量名
>>> list1 = ['a','b','c']
# 变量名也可以又被赋值给其他变量名
>>> list2 = list1
>>> list1 is list2
True
>>> id(list1)
140122643092424
>>> id(list2)
140122643092424
>>> 
#  当然函数也可以这样操作
>>> def f1():
...     print('当这个函数被调用时,此处代码才会被执行')
... 
# 可以将此函数赋值给其他的变量名
>>> func = f1
>>> print(f1)
<function f1 at 0x7f70d85d8510>
>>> print(func)
<function f1 at 0x7f70d85d8510>
>>> func is f1
True
>>> 从以上信息可以看出 func 就是 f1,那么此时调用 func() 就是在调用f1()
>>> func()
当这个函数被调用时,此处代码才会被执行
>>> 

  当定义一个函数,其实是执行了对一个变量名(即函数名)赋值的操作。

  

>>> def f1():
...     print("其实这里的函数体,在定义函数时,在内存里就是一串字符串")
... 
>>> 

>>> # 在内存中是这样的:f1 = 'print("其实这里的函数体,在定义函数时,在内存里就是一串字 
符串")'

'''只有在调用时,python 会对这串字符串编译成字节码,再由 python 去解释器这些字节码,
 ... 解释字节码是,会先检查字节码中那些是符合 python 的语法,再跟进语法执行。'''

2. 命名空间和作用域

 1 [root@localhost python3]# cat namespace.py 
 2 #!/usr/bin/env    python3
 3 # 这个脚本是用来展示命名空间和变量的作用域的
 4 x = 1
 5 print('主程序 x 的值:',x)
 6 print('主程序 x 的内存地址:',id(x))
 7 
 8 def f1():
 9     x = 10
10     print('函数 x 的值:',x)
11     print('函数 x 的内存地址:',id(x))
12     
13 f1()    
14 [root@localhost python3]# python3 namespace.py 
15 主程序 x 的值: 1
16 主程序 x 的内存地址: 9220192
17 函数 x 的值: 10
18 函数 x 的内存地址: 9220480
19 [root@localhost python3]#
20 
21 # 
View Code

 

二、高阶函数

  特点:

  • 1. 把一个函数的函数名当做实参传给另一个函数

    前面提到了,  函数即变量,定义一个函数,也就是定义一个变量而已。变量可以被当做参数来被函数使用,当然,函数也可以被当做参数被传递和使用

# 定义一个函数,打印 数字 521
>>> def answer():
 ...     print(521)
 ... 
# 再定义一个函数,这个函数有个形参 func
>>> def run_something(func):
...     func()                     # 调用被传进来的函数
... 
# 执行run_something函数时,把 answer函数的函数名当做一个实参传给这个函数
>>> run_something(answer)  
521
>>>

# 传的时候可不是传 answer()  而是 answer 函数的本身  
>>> def run_something(func):
...     print(func)
... 
# 传的是 answer(),那么这个函数将会先运行,之后传进去的只是函数的执行的结果,这样, run_something 这个函数
# 接收到的将是一个 None ,之后就会报错
>>> run_something(answer())
ok            # 函数先在解释器中运行了本身
None          # 之后输出了返回值
Traceback (most recent call last):   # 传递个使用它的函数时,传的是 None ,所以报错了
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in run_something
TypeError: 'NoneType' object is not callable
>>>

    再来看另一例子:

 1 >>> def add_args(a,b):
 2 ...     print(a + b)
 3 ... 
 4 >>> def run_somthing_with_args(func,arg1,arg2):
 5 ...     func(arg1,arg2)
 6 ... 
 7 >>> run_somthing_with_args(add_args,5,6)
 8  11
 9 >>> 
10 #   运行run_somthing_with_args(5,6,add_args)时:
11     5,6被赋值给 arg1,arg2;add_args被赋值给了 func;
12     而在函数 run_somthing_with_args 中,调用含参数的函数func(arg1,arg2),
13     而在此时,函数func 也就是add_args,而这两个参数arg1,arg2也就传给了 a 和 b,
14     即 a = arg1 = 5,b = arg2 = 6
15     在这里同样可以用上 *args,**kwargs  的技巧
16 >>> def sum_args(*args,**kwargs):
17 ...     print(args,kwargs)
18 ... 
19 >>> def run_with_args(func,*args,**kwargs):
20 ...     func(*args,**kwargs)
21 ... 
22 >>> run_with_args(sum_args,2,a=1)
23 (2,) {'a': 1}
24 >>> 
  • 2. 另一个函数返回值中包含一个函数的函数名

    函数可以返回任意数量的任何类型的数据,当然也包括某一个函数的本身

>>> def sum_args(*args):
...     print(sum(args))
... 
>>> def return_func(func):
...     return func
... 
>>> return_func(sum_args)
<function sum_args at 0x7f0eaeae0e18>   #可以看出执行函数,此时返回的只是一个对象,对象的名字就叫 sum_args
>>> f1 = return_func(sum_args)  # 把这个对象赋值个一个变量名,即 f1 = sum_args
>>> f1(7,8)     #调用f1(7,8)  也就是在调用 sum_args(7,8)
15
>>> 

三、嵌套函数

  嵌套函数有时也叫做内部函数,就是在一个函数的内部再定义一个函数

>>> def outer(a,b):
...         def inner(c,d):
...             return   c + d
...         return inner(a,b)
... 
>>> outer(5,6)
11
>>> 

# 来简单分析一下执行 outer(5,6)时的流程:
1. 执行 outer(5,6), 将5和6 传入函数outer 即:a = 5, b = 6
2. 在函数 outer 内部定义另一个函数 inner 即: inner = 'return c + b'
3. 最后返回一个值,这个值就是函数 inner的结果,同时把变量 a 和 b 当做实参传给了 inner 函数,
   即:c = a = 5 , d = b = 6 
   在 inner 函数内部也有个返回值 c + d ,此时也就是 5 + 6
4. 执行的结果就是 outer(5,6) = inner(5,6),结果就是 11
   

四、闭包函数

    闭包函数是另一个函数动态生成的函数,并且可以储存和使用或者改变闭包函数外创建的变量的值

>>> def outer(arg):
...     def inner():
...         return '我是内部的闭包函数,我在使用外部的变量:%s' % arg
...     return inner
... 
>>> outer("Im's outer arg")
<function outer.<locals>.inner at 0x7f0eaea35730>
>>> f1 = outer("Im's outer arg")
>>> f1()
"我是内部的闭包函数,我在使用外部的变量:Im's outer arg"
>>> 

函数 inner 使用了其外部函数的参数(变量) arg ,并且外部函数返回的是函数 inner 本身,而不是调用函数 inner 和函数inner的返回值
此时, inner函数就是一个闭包函数

>>> f1
<function outer.<locals>.inner at 0x7f0eaea35950>
>>>

五、装饰器

   装饰器中会会使用一下几个技巧

    ·   *args 和 ** kwargs

    ·   闭包

    ·   作为参数的函数

  • 1. 基本装饰器

import time
def inner():
    time.sleep(3)
    print('in then inner')


def timer(func):
    def wraper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('func run time id %s' %(stop_time - start_time))
    return wraper

decorator = timer(inner)   # 手工设置装饰器

decorator()

执行结果:
in then inner
func run time id 3.000171422958374

# 上面是手工赋值的方式;当然Python不会这么low,已经给你制定了一个很好的方试,
让你可以足够的装逼
import time

def timer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('func run time id %s' %(stop_time - start_time))
    return wrapper
@timer
#  这里的 @timer 就是Python提供的装逼技巧
def inner(): time.sleep(3) print('in then inner') inner()

    装饰器做了两件事:

    1. 执行装饰器函数,并且同时把要装饰的函数名,当做实参传入。

    2. 返回一个在装饰器内部定义的函数名,即一个对象;并且这个返回值含有了两个东西:

      a.  内部函数本身的功能

      b.  被装饰的函数执行结果

    当使用装饰器时又做了两件事:

    1. 执行装饰器函数

    2. 把装饰器的返回值又重新赋值给被装饰的函数;即 inner = wrapper

  • 2. 装饰器执行流程

  • 3. 装饰器之传参

  • 4. 装饰器之返回值

五、多层装饰器

六、内置函数

内置函数也叫内置方法,在 Python 已启动就加载到 Python 解释器里的。可以直接使用的一些函数。

下图中就是 Python 中全部的内置函数

值得注意的几个内置函数用法

#compile
f = open("函数递归.py")
data =compile(f.read(),'','exec')
exec(data)


#print
msg = "又回到最初的起点"
f = open("tofile","w")
print(msg,"记忆中你青涩的脸",sep="|",end="",file=f)


# #slice
# a = range(20)
# pattern = slice(3,8,2)
# for i in a[pattern]: #等于a[3:8:2]
#     print(i)
#
#


#memoryview
#usage:
#>>> memoryview(b'abcd')
#<memory at 0x104069648>
#在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存,
import time
for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = data
    while b:
        b = b[1:]
    print('bytes', n, time.time()-start)

for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    print('memoryview', n, time.time()-start)

七、生成器

八、迭代器

九、软件目录开发规范

原文地址:https://www.cnblogs.com/xiguatian/p/6392069.html