函数基础:

  • 面向对象:基本单元-->类>>>class
  • 面向过程:基本单元-->过程>>>def  :
  • 函数式编程:基本单元-->函数>>>def    ;调用-->fun()

注意:

(1)、函数里面最好写函数说明,方便别人阅读;

(2)、关键参数不能写在位置参数前面;eg:test(3,x=2,4)

(3)、全局变量和局部变量

函数与函数式编程:

特点:允许把函数本身作为参数传入了一个函数,还返回一个函数!

eg:var result = substract(multiply(add(1,2),3),4))  #(1+2)*3/4

#语法
def 函数名(参数1,参数2,参数3,...):
    '''注释'''
    函数体
    return 返回的值

#函数名要能反映其意义

eg:

import time

def logger():
    time_format = '%Y-%m-%d %X'
    time_current = time.strftime(time_format)
    with open('text.txt', 'a+') as f:
        f.write('welcome you...%s' % time_current)

def fun1():
    '''面向函数说明'''
    print("fun1...")
    logger()  # logger()必须先于fun1定义:

def fun2():
    '''面向过程说明'''
    print("fun2...")
    logger()  # logger()必须先于fun2定义:

fun1()
fun2()

函数使用的原则:先定义,再调用;

函数在定义阶段,只检测语法,不执行代码,也就说,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道

定义函数的三种形式


#1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印
#2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值
#3、空函数:设计代码结构


#1、位置参数:按照从左到右的顺序定义的参数
        位置形参:必选参数
        位置实参:按照位置给形参传值

#2、关键字参数:按照key=value的形式定义的实参
        无需按照位置为形参传值
        注意的问题:
                1. 关键字实参必须在位置实参右面
                2. 对同一个形参不能重复传值

#3、默认参数:形参在定义时就已经为其赋值
        可以传值也可以不传值,经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)
        注意的问题:
                1. 只在定义时赋值一次
                2. 默认参数的定义应该在位置形参右面
                3. 默认参数通常应该定义成不可变类型

#4、可变长参数:
        可变长指的是实参值的个数不固定
        而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs

        ===========*args===接受N个位置参数值转换为元组的形式========
        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,3,4,5)     #1 2 (3,4,5)

       
        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,*[3,4,5])   #1 2 (3,4,5)


        def foo(x,y,z):
            print(x,y,z)
        foo(*[1,2,3])        #1 2 3

        =======**kwargs===接受N个关键字参数转换为字典形式========
        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,a=1,b=2,c=3)  #1 2 {'b': 2, 'a': 1, 'c': 3}

        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,**{'a':1,'b':2,'c':3})  # 1 2 {'b': 2, 'a': 1, 'c': 3}


        def foo(x,y,z):
            print(x,y,z)
        foo(**{'z':1,'x':2,'y':3})  #2 3 1

        ===========*args+**kwargs===========

        def foo(x,y):
            print(x,y)

        def wrapper(*args,**kwargs):
            print('====>')
            foo(*args,**kwargs)

#5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
可以保证,传入的参数中一定包含某些关键字
        def foo(x,y,*args,a=1,b,**kwargs):
            print(x,y)
            print(args)
            print(a)
            print(b)
            print(kwargs)

        foo(1,2,3,4,5,b=3,c=4,d=5)
        结果:
            1 2
            (3, 4, 5)
            1
            3
            {'c': 4, 'd': 5}

函数对象:

函数是第一类对象,即函数可以当作数据传递


#1 可以被引用
#2 可以当作参数传递
#3 返回值可以是函数
#3 可以当作容器类型的元素


利用该特性,优雅的取代多分支的if


def foo():
    print('foo')

def bar():
    print('bar')

dic={
    'foo':foo,
    'bar':bar,
}
while True:
    choice=input('>>: ').strip()
    if choice in dic:
        dic[choice]()


函数的嵌套

函数的嵌套调用

def max(x,y):
    return x if x > y else y

def max4(a,b,c,d):
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3
print(max4(1,2,3,4))

 函数的嵌套定义

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

f1()

名称空间和作用域


#名称空间:存放名字的地方,三种名称空间,(x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)


名称空间的加载顺序


python test.py
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行test.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间


名字的查找顺序



局部名称空间--->全局名称空间--->内置名称空间

#需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例

# max=1
def f1():
    # max=2
    def f2():
        # max=3
        print(max)
    f2()
f1()
print(max)


 作用域



#1、作用域即范围
       - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
      - 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000
f3(f1())

#3、查看作用域:globals(),locals()

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间



闭包:



#内部函数包含对外部作用域而非全局作用域的引用

#提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路

        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素


意义与应用


#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来是传参,现在是包起来)
    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))


递归:

1、必须有明确的结束条件;

2、每递归一次,问题规模比上一次减少;

3、递归效率不高。递归层次过多会导致栈溢出;

递归分为两个阶段:递推,回溯

# salary(5)=salary(4)+300
# salary(4)=salary(3)+300
# salary(3)=salary(2)+300
# salary(2)=salary(1)+300
# salary(1)=100
#
# salary(n)=salary(n-1)+300     n>1
# salary(1) =100                n=1

def salary(n):
    if n == 1:
        return 100
    return salary(n-1)+300

print(salary(5))
python中的递归
    python中的递归效率低,需要在进入下一次递归时保留当前的状态;其他语言中的解决方法:尾递归优化,即在函数的最后一步(而非最后一行)调用自己,尾递归优化:http://egon09.blog.51cto.com/9161406/1842475
python又没有尾递归,且对递归层级做了限制。


三元表达式:

name=input('姓名>>: ')
res='girl' if name == 'julia' else 'boy'
print(res)

列表推导式:

#1、示例
egg_list=[]
for i in range(10):
    egg_list.append('鸡蛋%s' %i)

egg_list=['鸡蛋%s' %i for i in range(10)]

#2、语法
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
类似于
res=[]
for item1 in iterable1:
    if condition1:
        for item2 in iterable2:
            if condition2
                ...
                for itemN in iterableN:
                    if conditionN:
                        res.append(expression)

#3、优点:方便,改变了编程习惯,可称之为声明式编程

生成器表达式:

#1、把列表推导式的[]换成()就是生成器表达式

#2、示例:生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性
>>> chicken=('鸡蛋%s' %i for i in range(5))
>>> chicken
<generator object <genexpr> at 0x10143f200>
>>> next(chicken)
'鸡蛋0'
>>> list(chicken) #因chicken可迭代,因而可以转成列表
['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4',]

#3、优点:省内存,一次只产生一个值在内存中

高阶函数:

可以将函数作为参数传递给函数:

def sum_abc(x,y,f):
    return f(x)+f(y)

s = sum_abc(-2,3,abs)
print(s)  #5

内置函数:

中文查阅:http://www.runoob.com/python/python-built-in-functions.html

 

 

内置函数

 

 

abs()

dict()

help()

min()

setattr()

all()

dir()

hex()

next()

slice()

any()

divmod()

id()

object()

sorted()

ascii()

enumerate()

input()

oct()

staticmethod()

bin()

eval()

int()

open()

str()

bool()

exec()

isinstance()

ord()

sum()

bytearray()

filter()

issubclass()

pow()

super()

bytes()

float()

iter()

print()

tuple()

callable()

format()

len()

property()

type()

chr()

frozenset()

list()

range()

vars()

classmethod()

getattr()

locals()

repr()

zip()

compile()

globals()

map()

reversed()

__import__()

complex()

hasattr()

max()

round()

 

delattr()

hash()

memoryview()

set()

 

注:

all(iterable)  : iterable里面的元素均为True,则结果为True;

any(iterable) :iterable里面的任意元素为True,则结果为True,若iterable为empty,返回False;

装饰器:

装饰器就是闭包函数的一种应用场景,本质是函数

WHY

#开放封闭原则:对修改封闭,对扩展开放

WHAT

装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。

强调装饰器的原则:

1 不修改被装饰对象的源代码

2 不修改被装饰对象的调用方式

装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

HOW

知识储备:

1、函数即“变量”;

回收机制:没有应用,就立马回收。

主函数执行之前,顺序无影响(类比变量顺序)。

2、高阶函数;

a、把一个函数作为实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能);

b、返回值中包含函数名(不修改函数的调用方式);

bar():  bar内存地址(门牌号);bar()基于内存地址在调用

3、嵌套函数

在一个函数的函数体内,用def声明一个函数,并不是调用。


语法:高阶函数+嵌套函数==>装饰器

被装饰函数的正上方,单独一行
        @deco1
        @deco2
        @deco3
        def foo():
            pass

        foo=deco1(deco2(deco3(foo)))



无参装饰器

有参装饰器

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()



def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')



补充:wraps

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)

迭代器:


生成器:


面向过程编程:


Json和pickle序列化:

把变量从内存中变成可存储或可传输的过程就称之为序列化。在Python中称为picking,在其他语言中称之为serialization,marshalling,flattening等等,都是一个意思。序列化之后就可以存储到磁盘上,或通过网络传输到别的机器上。有序列化就有反序列化,把内容从序列化对象重新读取到内存的过程称之为反序列化。

json与pickle的区别

  • json只能处理基本数据类型;pickle能处理所有的Python数据类型
  • json多用于各个语言之间的网络传输;pickle多用于Python程序对象的持久化或Python程序之间的网络传输

json

  • 序列化
import json
d = {"a":1,"b":2}
f = open("test.txt","wb")
s_dict = json.dumps(d)      # 步骤1:序列化
f.write(s_dict)             # 步骤2:写入磁盘
f.close()

步骤1,步骤2等价于:
json.dump(d,f)
  • 反序列化
import json
f = open("test.txt","r")
d = json.loads(f.read())    # 步骤1:反序列化。此时d为dict
f.close()

步骤1等价于:
d = json.load(f)    # 此时d为dict
f.close()

pickle

  • 序列化
import pickle
d = {"a":1,"b":2}
f = open("test.txt","wb")

s_dict = pickle.dumps(d)    # 步骤1: 序列化
f.write(s_dict)         # 步骤2:将序列化后的内容存储到磁盘,因为使用pickle序列化之后变成了一个byte,所以写入方式为byte,所以写入的数据看起来像是乱码的
f.close()

步骤1,步骤2等价于:
pickle.dump(d,f)
  • 反序列化
import pickle
f = open("test.txt","rb")
d = pickle.loads(f.read())  # 步骤1:反序列化。此时d为dict
f.close()

步骤1等价于
d = pickle.load(f)      # d 为dict
f.close()
总结:

json   用于字符串和python数据类型间进行转换
pickle 用于python特有的类型 和 python的数据类型间进行转换

序列化:把字典或者字符串的内存对象 存到硬盘上;
反序列化:就是从硬盘上加载出来

当json dumps多次的时候,loads的时候报错;同时也不可以loads多次;
要想loads多次,就在dumps的时候dumps成不同的文件,laods不同文件。

当pickle dumps多次的时候,loads的时候不报错,但得出的结果是第一次dumps的内容;同时也不可以loads多次;
要想loads多次,就在dumps的时候dumps成不同的文件,laods不同文件。