第五章函数

第五章 函数

5.1 函数的本质及应用场景

截至目前:面向过程编程(可读性差/可重用性差)

对于函数编程:

  1. 本质:将N行代码拿到别处并给他起一个名字,以后通过名字就可以找到这段代码并执行
  2. 应用场景:
    • 代码重复执行
    • 代码特别多超过一屏,可以选择通过函数进行代码的分割
# 面向过程编程 
user_input = input('请输入角色:')

if user_input == '管理员':    
    import smtplib    
    from email.mime.text import MIMEText    
    from email.utils import formataddr
    msg = MIMEText('管理员,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')    
    msg['From'] = formataddr(["李邵奇", '[email protected]'])    
    msg['To'] = formataddr(["管理员", '[email protected]'])    
    msg['Subject'] = "亲爱的管理员"
    
    server = smtplib.SMTP("smtp.163.com", 25)           
    server.login("[email protected]", "qq1105400511")          
    server.sendmail('[email protected]', ['管理员', ], msg.as_string())
    server.quit() 
    
elif user_input == '业务员':    
    import smtplib    
    from email.mime.text import MIMEText    
    from email.utils import formataddr
    msg = MIMEText('业务员,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')    
    msg['From'] = formataddr(["李邵奇", '[email protected]'])    
    msg['To'] = formataddr(["业务员", '业务员'])    
    msg['Subject'] = "亲爱的业务员"
    
    server = smtplib.SMTP("smtp.163.com", 25)    
    server.login("[email protected]", "qq1105400511")   
    server.sendmail('[email protected]', ['业务员', ], msg.as_string())    
    server.quit() 
    
elif user_input == '老板':    
    import smtplib    
    from email.mime.text import MIMEText    
    from email.utils import formataddr
    
    msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')    
    msg['From'] = formataddr(["李邵奇", '[email protected]'])    
    msg['To'] = formataddr(["老板", '老板邮箱'])    
    msg['Subject'] = "亲爱的老板"
    
    server = smtplib.SMTP("smtp.163.com", 25)    
    server.login("[email protected]", "qq1105400511")    
    server.sendmail('[email protected]', ['老板邮箱', ], msg.as_string())    
    server.quit()
#函数式编程
#先不考虑是否能执行,单纯为了比较函数式编程与面向过程编程的区别
def send_email():    
    import smtplib    
    from email.mime.text import MIMEText    
    from email.utils import formataddr
    
    msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')    
    msg['From'] = formataddr(["李邵奇", '[email protected]'])    
    msg['To'] = formataddr(["老板", '老板邮箱'])    
    msg['Subject'] = "情爱的老板"
    
    server = smtplib.SMTP("smtp.163.com", 25)    
    server.login("[email protected]", "qq1105400511")    
    server.sendmail('[email protected]', ['老板邮箱', ], msg.as_string())
    server.quit()

user_input = input('请输入角色:')
if user_input == '管理员':    
    send_email() 
elif user_input == '业务员':    
    send_email() 
elif user_input == '老板':    
    send_email()

5.2 函数定义

5.2.1 基本定义函数

  1. 基本形式

    def 函数名():       #函数的定义
        代码            #函数内容
    
    函数名()            #函数的执行
  2. 注意

    • 函数如果不被调用,则内部代码永远不会被执行
    • len(计算长度)是python内部写好的一个函数

5.2.2 参数(个数不限制)

5.2.2.1 基本参数知识

def get_list_date(aaa):       #aaa:形式参数(形参)  任意个数
    v = [11,22,33,44]
    print(v[aaa])
    
get_list_date(1)             #1:实际参数(实参)   任意类型
  • 发送邮件问题

    # 假如:管理员/业务员/老板用的是同一个邮箱。 
    def send_email(to):    
        import smtplib    
        from email.mime.text import MIMEText    
        from email.utils import formataddr
    
        msg = MIMEText('导演,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')    
        msg['From'] = formataddr(["李邵奇", '[email protected]'])    
        msg['To'] = formataddr(["导演", to])    
        msg['Subject'] = "情爱的导演"
    
        server = smtplib.SMTP("smtp.163.com", 25)    
        server.login("[email protected]", "qq1105400511")  
        server.sendmail('[email protected]', [to, ], msg.as_string())    
        server.quit() 
    
    def send_email(to):    
        template = "要给%s发送邮件" %(to,)    
        print(template)
    
    user_input = input('请输入角色:')
    if user_input == '管理员':    
        send_email('[email protected]') 
    elif user_input == '业务员':    
        send_email('[email protected]') 
    elif user_input == '老板':    
        send_email('[email protected]')

5.2.2.2 位置传参

  • 传参:调用函数并传入参数
  • 要求:严格按照位置进行传参
  • 位置参数:positional argument
  • 基本形式
def func(a,b):
    print(a,b)
    
func(11,22)               #位置都是一一对应的,a = 11,b = 22

5.2.2.3 关键字传参

  • 关键字参数:keyword argument

  • 基本形式

    def func(a,b):
        print(a,b)
    
    func(b = 11,a = 22)        #按照关键字进行传参,可以交换位置
  • 注意:open(打开文件)是python内部写好的一个函数,运用的就是关键字传参

  • 关键字传参和位置传参可以混合使用

    • 位置参数必须在关键字参数的前面
    • 位置参数的个数 + 关键字参数的个数 = 总参数个数
    • 一个参数不能传多次

5.2.2.4 默认参数:可传可不传

  • 基本形式

    def func(a,b = 9):          #b就是默认参数,默认值为9
    #func函数接受两个参数,调用函数进行传值时,默认参数可传可不传,不传则使用默认值,传则使用传的值
        print(a,b)
    
    func(123)                   #a = 123,b使用默认值,即b = 9
    func(11,22)                 #a = 11,b = 22
  • 对于默认值,如果是可变类型(对于函数的默认值慎用可变类型)

    扫描二维码关注公众号,回复: 7262221 查看本文章
    # 如果要想给value设置默认是空列表
    
    # 不推荐(坑)
    def func(data,value=[]): 
        pass 
    
    # 推荐
    def func(data,value=None):
        if not value:
            value = []
  • 面试题

    def func(a,b=[]):
        b.append(a)
        return b
    
    l1 = func(1)
    l2 = func(2,[11,22])
    l3 = func(3)
    
    # [1,3]   [11,22,2]   [1,3]
    print(l1,l2,l3)
    

5.2.2.5 万能参数(*args / **kwargs)

  • *args

    • 可以接受任意个数的位置参数,并将参数转化为元组(注意实参里有*和没有的区别)
    • 只能使用位置传参
    • 基本形式
    def func(*args):
        print(args)
    
    func(1,2)                #args = (1,2)
    func((11,22,33))         #args = ((11,22,33),)    
    func(*(11,22,33))        #args = (11,22,33)       循环元组里的元素,加入到元组中
    func(*[11,22,33,44])     #args = (11,22,33,44)    循环列表里的元素,加入到元组中
    #注意实参里有*和没有的区别
    
    #特殊情况:
    def func(a,*args,b):     #a只能使用位置参数,b只能使用关键字参数
        print(a,args,b)
    
    func(1,2,3,4,b = 5)      #a = 1,args = (2,3,4),b = 5  
    
  • **kwargs

    • 可以接受任意个数的关键字参数,并将参数转化为字典(注意实参里有**和没有的区别)
    • 只能使用关键字传参
    • 基本形式
    def func(**kwargs):
        print(kwargs)
    
    func(k1 = 1,k2 = 'alex')          #kwargs = {'k1':1,'k2':'alex'}
    func(**{'k1':'v1','k2':'v2'})     #kwargs = {'k1':'v1','k2':'v2'}    
    #注意实参里有**和没有的区别  
    
  • 综合运用(*args和**kwargs一起使用)

def func(*args,**kwargs):
    print(args,kwargs)
    
func(1,2,3,4,k1 = 1,k2 = 2)              #args = (1,2,3,4),kwargs = {'k1':1,'k2':2}
func(*[1,2,3],**{'k1':'v1','k2':'v2'})   #args = (1,2,3),kwargs = {'k1':'v1','k2':'v2'} 
  1. 参数相关重点

    • 定义函数

      #情况一:
      def func(a,b):
          pass
      
      #情况二:
      def func(a,b = None):
          pass
      
      #情况三:
      def func(*args,**kwargs):
          pass
      
    • 调用函数:位置参数 > 关键字参数

  2. 练习题

# 1. 请写一个函数,函数计算列表 info = [11,22,33,44,55] 中所有元素的和。
def get_sum():
    info = [11,22,33,44,55]
    data = 0
    for item in info:
        data += item
    print(data)

get_sum()

# 2. 请写一个函数,函数计算列表中所有元素的和。
def get_list_sum(a1):
    data = 0
    for item in a1:
        data += item
    print(data)
    
get_list_sum([11,22,33])
get_list_sum([99,77,66])
v1 = [8712,123,123]
get_list_sum(v1)

# 3. 请写一个函数,函数将两个列表拼接起来。
def join_list(a1,a2):
    result = []
    result.extend(a1)
    result.extend(a2)
    print(result)
    
join_list([11,22,33],[55,66,77]

# 4. 计算一个列表的长度
def my_len(arg):
    count = 0
    for item in arg:
          count += 1
    print(count)

v = [11,22,33]
my_len(v)
len(v)

# 5. 发邮件的示例        
def send_email(role,to):
    template = "要给%s%s发送邮件" %(role,to,)
    print(template)
 
user_input = input('请输入角色:')
if user_input == '管理员':
    send_email('管理员','[email protected]')
elif user_input == '业务员':
    send_email('业务员','[email protected]')
elif user_input == '老板':
    send_email('老板','[email protected]')

5.2.3 返回值(return)

  1. 作用
    • 返回值
    • 终止函数的执行
  2. 基本形式
def func(arg):
    代码             #函数内容
    return 9         #返回值为9,默认:return None

val = func('ads')    #设置一个变量接收返回值
print(val) #9
#特殊情况:
def func():
    return 2,3,'alex'     #返回是一个元组

val = func()
print(val)                #(2,3,'alex')
  1. 返回值相关重点
    • 函数没有返回值,默认返回:None
    • 函数内部执行过程中一旦遇到return就终止
    • return可以返回任意类型
# 练习题:

# 1. 写函数,计算一个列表中有多少个数字,打印: 列表中有%s个数字。
#    提示:type('x') == int 判断是否是数字。
# 方式一:
def get_list_counter1(data_list):
    count = 0
    for item in data_list:
        if type(item) == int:
            count += 1
    msg = "列表中有%s个数字" %(count,)
    print(msg)
    
get_list_counter1([1,22,3,'alex',8])

# 方式二:
def get_list_counter2(data_list):
    count = 0
    for item in data_list:
        if type(item) == int:
            count += 1
    return count
    
v = get_list_counter1([1,22,3,'alex',8])
msg = "列表中有%s个数字" %(v,)
print(msg)

# 2. 写函数,计算一个列表中偶数索引位置的数据构造成另外一个列表,并返回。
# 方式一:
def get_data_list1(arg):
    v = arg[::2]
    return v

data = get_data_list1([11,22,33,44,55,66])

# 方式二:
def get_data_list2(arg):
    v = []
    for i in range(0,len(arg)):
        if i % 2 == 0:
            v.append(arg[i])
    return v

data = get_data_list2([11,22,33,44,55,66])

# 3. 读取文件,将文件的内容构造成指定格式的数据,并返回。
"""
a.log文件
    alex|123|18
    eric|uiuf|19
    ...
目标结构:
a.  ["alex|123|18","eric|uiuf|19"] 并返回。
b. [['alex','123','18'],['eric','uiuf','19']]
c. [
    {'name':'alex','pwd':'123','age':'18'},
    {'name':'eric','pwd':'uiuf','age':'19'},
 ]
"""
def a():
    info = []
    with open('a.log',mode='r',encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            info.append(line)
    return info

def b():
    info = []
    with open('a.log',mode='r',encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            v = line.split('|')
            info.append(v)
    return info

def c():
    info = []
    with open('a.log',mode='r',encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            list = line.split('|')
            v = {}
            v['name'] = list[0]
            v['pwd'] = list[1]
            v['age'] = list[2]
            info.append(v)
    return info

date1 = a()
date2 = b()
date3 = c()
print(date1,date2,date3)
  1. 数据类型中的方法有没有返回值

    • 无返回值

      #示例:
      v = [11,22,33]
      v.append(99) 
      
    • 仅有返回值:

      #示例一:
      v = "alex"
      result = v.split('l')
      
      #示例二:
      v = {'k1':'v2'}
      result1 = v.get('k1')
      result2 = v.keys()
      
    • 有返回+修改数据

      #示例:
      v = [11,22,33]
      result = v.pop()          # 将被删除的数据赋值给result
      
    • 常用需记住的

      • str
        • strip,返回字符串
        • split,返回列表
        • replace,返回字符串
        • join,返回字符串
      • list
        • append,无
        • insert,无
        • pop,返回要删除的数据
        • remove,无
        • find/index,返回索引的位置的数据
      • dict
        • get,返回索引键的值
        • keys,返回字典的所有键
        • values,返回字典的所有值
        • items,返回字典所有的键值对

5.2.4 总结

  1. 函数的基本结构:
#情况一
def f1():
    函数内容
f1()

#情况二
def f2(a):
    函数内容    
f2(1)

#情况三
def f3():
    函数内容
    return 
v1 = f3()

#情况四
def f4(a1,a2):
    函数内容
    return a1+a2
v2 = f4(1,7)
  1. 函数内部的数据是否会混乱?
    • 函数内部执行相互之间不会混乱
    • 执行完毕 + 内部元素不被其他人使用 => 销毁

5.3 函数小高级

5.3.1 函数名可以当作变量来使用

#示例一:
def func():
    print(123)
    
func_list = [func, func, func]
# func_list[0]()
# func_list[1]()
# func_list[2]()
for item in func_list:
    v = item()
    print(v)
    
#示例二:
def func():
    print(123)
def bar():
    print(666)

info = {'k1': func, 'k2': bar}
info['k1']()
info['k2']()

#注意区别:
def func():
    return 123

func_list1 = [func,func,func]
func_list2 = [func(),func(),func()]
print(func_list1)
print(func_list2)

5.3.2 函数可以当作参数进行传递

#示例:
def func(arg):
    v1 = arg()
    print(v1)    
def show():
    print(666)
    
result = func(show)
print(result)
#面试题:多个函数的调用
def func():
    print('话费查询')
def bar():
    print('语音沟通')
def base():
    print('业务办理')
def show():
    print('流量查询')
def test():
    print('人工服务')

info = {
    'f1': func,
    'f2': bar,
    'f3':base,
    'f4':show,
    'f5':test
}
choice = input('请选择要选择功能:')
function_name = info.get(choice)
if function_name:
    function_name()
else:
    print('输入错误')

5.3.3 匿名函数: lambda表达式

  1. 作用:用于表达简单的函数

  2. 基本形式:

    #三元运算,为了解决简单的if else的情况
    if 1 == 1:
        a = 123
    else:
        a = 456
    #用三元运算表示:
    a =  123  if 1 == 1 else 456
    
    #lambda表达式,为了解决简单函数的情况
    def func(a1,a2):
        return a1 + a2 
    #用lambda表达式表示:
    func = lambda a1,a2: a1+a2          #隐含了return
    
  3. 重点:

    • lambda表达式只能用一行表示函数
    • 用lambda表达式表示函数时,无法设置新变量,只能用参数作变量
    • 列表所有方法基本上都是返回None,字符串的所有方法基本上都是返回新值
  4. 练习题

    # 练习题1
    USER_LIST = []
    def func0(x):
        v = USER_LIST.append(x)
        return v 
    result = func0('alex')
    print(result)              #None
    
    # 练习题2
    def func0(x):
        v = x.strip()
        return v 
    result = func0(' alex ')
    print(result)               #'alex'
    
    # 练习题3
    USER_LIST = []
    func1 = lambda x: USER_LIST.append(x)
    v1 = func1('alex')
    print(v1)                    #None
    print(USER_LIST)             #['alex']
    
    # 练习题4
    func1 = lambda x: x.split('l')
    v1 = func1('alex')
    print(v1)                    #['a','ex']
    
    # 练习题5
    func_list = [lambda x:x.strip(), lambda y:y+199,lambda x,y:x+y]
    v1 = func_list[0]('alex ')
    print(v1)                    #'alex'
    v2 = func_list[1](100)
    print(v2)                    #299
    v3 = func_list[2](1,2)
    print(v3)                    #3
    

5.4 函数中高级

5.4.1 函数可以做返回值

#示例:
def func():
    print(123)
def bar():
    return func

v = bar()
v()

5.4.2 闭包

  1. 概念:为函数创建一块区域并为其维护自己数据,以后执行方便调用
  2. 应用场景:
    • 装饰器
    • SQLAlchemy源码
#示例:
name = 'oldboy'
def bar(name):
    def inner():
        print(name)
    return inner
v1 = bar('alex') # { name=alex, inner }  # 闭包,为函数创建一块区域(内部变量供自己使用),为他以后执行提供数据。
v2 = bar('eric') # { name=eric, inner }
v1()
v2()

#区分:
# 不是闭包
def func1(name):
    def inner():
        return 123
    return inner 

# 是闭包:封装值 + 内层函数需要使用。
def func2(name):
    def inner():
        print(name)
        return 123
    return inner 
#练习题:
#第一题
name = 'alex'
def base():
    print(name)
def func():
    name = 'eric'
    base()
func() 
    
# 第二题
name = 'alex'
def func():
    name = 'eric'
    def base():
        print(name)
    base()
func()

# 第三题
name = 'alex'
def func():
    name = 'eric'
    def base():
        print(name)
    return base 
base = func()
base()

#注意:函数在何时被谁创建?
#示例:
info = []
def func():
    print(item)   
for item in range(10):
    info.append(func)

info[0]()


#面试题
info = []
def func(i):
    def inner():
        print(i)
    return inner
for item in range(10):
    info.append(func(item))

info[0]()
info[1]()
info[4]()

5.4.3 高阶函数

  1. 把函数当作参数传递
  2. 把函数当作返回值
  3. 注意:对函数进行赋值

5.5 内置函数

函数分为自定义函数和内置函数

python内置函数分类:

5.5.1 强制转换

  • int() / str() / bool() / list() / tuple() / dict() / set()

5.5.2 输入输出

  • input() / print()

5.5.3 数学相关

  • abs():计算绝对值

  • sum():求和

  • float():转化为浮点型(小数)

    v = 55
    v1 = float(55)
    print(v1)        #55.0
    
    • 补充:int(55.5) #只保留整数:55
  • max():找到最大值

  • min():找到最小值

  • divmod():取两数相除的商和余数

    #示例:
    a,b = divmod(1001,5)
    print(a,b)          ##a=200,b=1
    
    
    • 注意:divmod在分页展示时用得比较多

      # 练习题  请通过分页对数据进行展示
      """
      要求:
          每页显示10条数据
          让用户输入要查看的页面:页码
      """
      
      USER_LIST = []
      for i in range(1,836):
          temp = {'name':'你-%s' %i,'email':'123%[email protected]' %i }
          USER_LIST.append(temp)
      
      # 数据总条数
      total_count = len(USER_LIST)
      
      # 每页显示10条
      per_page_count= 10
      
      # 总页码数
      max_page_num,a = divmod(total_count,per_page_count)
      if a>0:
          max_page_num += 1
      
      while True:
          pager = int(input('要查看第几页:'))
          if pager < 1 or pager > max_page_num:
              print('页码不合法,必须是 1 ~ %s' %max_page_num )
          else:
              start = (pager-1) * per_page_count
              end = pager * per_page_count
              data = USER_LIST[start:end]
              for item in data:
                  print(item)
      
  • pow():指数

    v = pow(2,3)      #相当于2**3
    print(v)         # 8
    
  • round():保留几位小数,默认保留整数,还会四舍五入

    v = round(1.127,2)       #第二个数代表保留几位小数,默认是None
    print(v)         # 1.13  四舍五入
    

5.5.4 进制转换相关

  • bin():将十进制转换成二进制

  • oct():将十进制转换成八进制

  • int():将其他进制转换成十进制

    • 默认:base = 10
    # 二进制转化成十进制
    v1 = '0b1101'
    result = int(v1,base=2)         #base=2说明读取的是二进制
    print(result)
    
    # 八进制转化成十进制
    v1 = '0o1101'
    result = int(v1,base=8)
    print(result)
    
    # 十六进制转化成十进制
    v1 = '0x1101'
    result = int(v1,base=16)
    print(result)
    
  • hex():将十进制转换成十六进制

#1字节等于8位
# IP: 192.168.12.79  ->  001010010 . 001010010 . 001010010 . 001010010

# 请将 ip = "192.168.12.79" 中的每个十进制数转换成二进制并通过,连接起来生成一个新的字符串。
ip = "192.168.12.79"
ip_list = ip.split('.') # ['192','168','12','79']
result = []
for item in ip_list:
    result.append(bin(int(item)))
print(','.join(result))


#面试题:
# 请将 ip = "192.168.12.79" 中的每个十进制数转换成二进制: 
#          0010100100001010010001010010001010010 -> 十进制的值。
# 3232238671
ip = '192.168.12.79'
v = ip.split('.')
info = []
for i in v:
    date = str(bin(int(i)))
    if len(date) > 10:
        date = date[-8:]
    else:
        count = 10 - len(date)
        date = date.replace('0b','0'*count)
    info.append(date)
val = "".join(info)
a = int(val,base=2)
print(a)

5.5.5 编码相关

  • chr():将十进制数字转换成 unicode 编码中的对应字符串

  • ord():根据字符在 unicode 编码中找到其对应的十进制

    #应用:生成随机验证码
    import random          # 导入一个模块 
    def get_random_code(length=6):
        data = []
        for i in range(length):
            v = random.randint(65,90)
            data.append(chr(v))
        return  ''.join(data)
    
    code = get_random_code()
    print(code)
    

5.5.6 内置函数高级

  • map(函数,可迭代对象):一起执行

    • 循环每个元素(第二个参数),然后让每个元素执行函数(第一个参数),将每个函数执行的结果保存到新的列表中,并返回
    #示例:
    v1 = [11,22,33,44]
    result = map(lambda x:x+100,v1)
    print(list(result)) 
    
  • filter(函数,可迭代对象):进行筛选

    #示例:
    v1 = [11,22,33,'asd',44,'xf']
    
    def func(x):
        if type(x) == int:
            return True
        return False
    result = filter(func,v1) 
    print(list(result))
    
    # 用lambda表达式:
    result = filter(lambda x: True if type(x) == int else False ,v1)
    print(list(result))
    # 相当于:
    result = filter(lambda x: type(x) == int ,v1)
    print(list(result))
    
  • map / filter(python2与python3的区别九)

    • python2:返回列表,直接创建值,可以通过索引取值
    • python3:返回迭代器,不直接创建值,通过循环,边循环边创建
  • reduce(函数,可迭代对象):得到结果

    import functools
    v1 = ['wo','hao','e']
    def func(x,y):
        return x+y
    result = functools.reduce(func,v1) 
    print(result)
    
    # 用lambda表达式:
    result = functools.reduce(lambda x,y:x+y,v1)
    print(result)
    

5.5.7 类相关

  • type():查看数据类型

    class Foo:
        pass
    
    obj = Foo()
    if type(obj) == Foo:
        print('obj是Foo类的对象')
    
  • issubclass(类,类或其基类):判断前一个类是否是后一个类或其基类的子类

    • 结果是布尔类型,是 -> True,否 -> False
    class Base:
        pass
    
    class Base1(Base):
        pass
    
    class Foo(Base1):
        pass
    
    class Bar:
        pass
    
    print(issubclass(Bar,Base))
    print(issubclass(Foo,Base))
    
  • isinstance(对象,类或其基类):判断一个对象是否是一个类或其基类的实例(对象)

    • 结果是布尔类型,是 -> True,否 -> False
    • 注意:判断一个对象是否是一个类的实例(对象),一定要用type,不要用isinstance
    class Base(object):
        pass
    
    class Foo(Base):
        pass
    
    obj = Foo()
    print(isinstance(obj,Foo))  # 判断obj是否是Foo类或其基类的实例(对象)
    print(isinstance(obj,Base)) # 判断obj是否是Foo类或其基类的实例(对象)
    
  • super().方法名():根据self对象所属的类的继承关系,按照顺序依次找方法并执行(找到第一个为止)

    class Base(object):
        def func(self):
            super().func()  # 根据self对象所属类的继承关系,按照顺序挨个找func方法并执行(找到第一个为止)
            print('base.func')
    
    class Bar(object):
        def func(self):
            print('bar.func')
    
    class Foo(Base,Bar): # Foo -> Base -> Bar
        pass
    
    obj = Foo()
    obj.func()
    

5.5.8 其他

  • len() :计算长度
  • open() :打开文件
  • range()
  • id() :查看内存地址

5.7 作用域

5.7.1 分类

  • 全局作用域:在python中,相当于一个py文件

    • 全局作用域中的变量称为全局变量
    • 全局变量必须全用大写
  • 局部作用域:在python中,相当于一个函数

    • 局部作用域中的变量称为局部变量
    • 局部变量则用小写
    USER_LIST = [11,22,3]         #全局变量
    def func():
        name = 'asdf'             #局部变量
        USER_LIST.append(12)
        USER_LIST.append(name)
    func()
    print(USER_LIST)
    

5.7.2 递归

  • 函数自己调用自己。(效率低)

    def func():
        print(1)
        func()
    
    func()
    
    # 递归的返回值
    def func(a):
        if a == 5:
            return 100000
        result = func(a+1) + 10
    v = func(1)
    
    name = 'alex'
    def func():
        def inner():
            print(name)
         return inner
    v =func()
    

5.7.3 总结

  • 在python中,一个函数就是一个作用域

  • 作用域中查找数据规则:

    • 优先在自己的作用域里找数据
    • 自己没有就去 '父级' 中找,没有就去 '父级' 的 '父级' 中找,直到找至全局作用域
    • 如果最后全局作用域中也没有,就会报错
    • 寻找时注意:父级作用域中的值到底是什么
    #示例一:
    x = 10
    def func():
        x = 9
        print(x)           
        def x1():
            x = 8
            print(x)
    func()               # 9
    
    #示例二:
    x = 10
    def func():
        x = 9
        print(x)
        def x1():
            x = 8
            print(x)
        x1()
    func()               # 9   8
    
    #示例三:
    x = 10
    def func():
        x = 9
        print(x)
        def x1():
            x = 8
            print(x)
        print(x)
        x1()
    func()               # 9   9   8   
    
    #示例四:
    x = 10
    def func():
        x = 9
        print(x)
        def x1():
            x = 8
            print(x)
        x1()
        print(x)
    func()               # 9   8   9 
    
    #示例五:
    x = 10
    def func():
        x = 9
        print(x)
        def x1():
            print(x)
        x1()
        print(x)
    func()               # 9   9   9
    
    #示例六:
    x = 10
    def func():
        x = 8
        print(x)
        def x1():
            print(x)
        x = 9
        x1()
        x = 10
        print(x)
    func()               # 8   9   10
    
    #示例七:
    x = 10
    def func():
        x = 8
        print(x)
        def x1():
            print(x)
        x1()
        x = 9
        x1()
        x = 10
        print(x)
    func()               # 8   8   9   10
    
  • 对于子作用域:

    • 只能找到 '父级' 中的值,默认无法重新为 '父级' 的变量进行赋值

    • 如果非要对变量进行重新赋值,使用 global / nonlocal 进行强制赋值

      • global:直接找到全局的变量,再进行重新赋值,其他的不更改
      • nonlocal:只找到 '父级' 的变量,进行重新赋值,其他的不更改
      • 建议:一般不要使用,以免造成代码混乱
      #global示例:
      name = 'oldboy'
      def func():
          name = 'alex'
          def inner():
              global name      #直接找到全局的name
              name = 'abc'     #再进行重新赋值
          inner()
          print(name)          #'alex'
      func()
      print(name)              #'abc'
      
      #nonlocal示例:
      name = "oldboy"
      def func():
          name = 'alex'
          def inner():
              nonlocal name    #找到上一级的name
              name = 'abc'     #再进行重新赋值
          inner()
          print(name)          #'abc'
      func()
      print(name)              #"oldboy"
      

5.8 推导式

5.8.1 列表推导式

# 目的:方便的生成一个列表
# 格式:
v1 = [i for i in 可迭代对象 ]
v2 = [i for i in 可迭代对象 if 条件 ]      #条件为true才进行append
# 示例:
v1 = [ i for i in 'alex' ]  
v2 = [i+100 for i in range(10)]
v3 = [99 if i>5 else 66  for i in range(10)]

def func():
    return 100
v4 = [func for i in range(10)]

v5 = [lambda : 100 for i in range(10)]
result = v5[9]()

v6 = [lambda :i for i in range(10)]
result = v6[5]()

# 新浪微博面试题
v7 = [lambda x:x*i for i in range(10)] 
# 1.请问 v7 是什么?
# 2.请问 v7[0](2) 的结果是什么?

# 面试题
def num():
    return [lambda x:i*x for i in range(4)]
# num() -> [函数,函数,函数,函数]
print([ m(2) for m in num() ]) # [6,6,6,6]

5.8.2 集合推导式

# 目的:方便的生成一个集合
# 格式:
v1 = { i for i in 'alex' }

5.8.3 字典推导式

# 目的:方便的生成一个字典
# 格式:
v1 = { 'k'+str(i):i for i in range(10)}

5.8.4 生成器推导式

# 列表推导式,立即循环创建所有元素
"""
def func():
    result = []
    for i in range(10):
        result.append(i)
    return result
v1 = func()
"""
v1 = [i for i in range(10)]
print(v1)

# 生成器推导式,创建了一个生成器,内部循环为执行
"""
def func():
    for i in range(10):
        yield i
v2 = func()
"""
v2 = (i for i in range(10))
for item in v2:
    print(item)

# 面试题:请比较 [i for i in range(10)] 和 (i for i in range(10)) 的区别?

5.9 装饰器

5.8.1 目的

  • 在不改变原函数内部代码的基础上,在函数执行之前和之后自动执行某个功能

5.8.2 应用场景

  • 想要为函数扩展功能时,可以选择用装饰器

5.8.3 基本装饰器

  1. 基本格式:

    def func(arg):
        def inner():
            v = arg()
            return v 
        return inner 
    
    # 重点:
    # 第一步:执行func函数并将下面的函数参数传递,相当于:func(index)
    # 第二步:将func的返回值重新赋值给下面的函数名。 index = func(index)
    @func 
    def index():
        print(123)
        return 666
    
    print(index)
    
  2. 总结

    • 编写格式:
    def 外层函数(参数):
        def 内层函数(*args,**kwargs)
         return 参数(*args,**kwargs)
        return 内层函数
    
    • 应用格式:
    @外层函数
    def index():             #要装饰的函数
        pass
    
    index()
    
    # 装饰器的编写
    def x(func):
        def y():
            # 前
            ret = func()
            # 后
            return ret 
         return y 
    
    # 装饰器的应用
    @x
    def index():
        return 10
    
    # 执行函数,自动触发装饰器了
    v = index()
    print(v)
    
  3. 示例:

    def func(arg):
        def inner():
            print('before')
            v = arg()
            print('after')
            return v 
        return inner 
    
    def index():
        print('123')
        return '666'
    
    # 示例一
    v1 = index() # 执行index函数,打印123并返回666赋值给v1.
    
    # 示例二
    v2 = func(index) # v2是inner函数,arg=index函数
    index = 666 
    v3 = v2()
    
    # 示例三
    v4 = func(index)
    index = v4  # index ==> inner 
    index()
    
    # 示例四
    index = func(index)
    index()
    

5.8.4 带参数的装饰器

  1. 应用场景:flask框架 / django缓存 / 写装饰器实现被装饰的函数要执行N次

    # 第一步:执行 ret = xxx(index)
    # 第二步:将返回值赋值给 index = ret 
    @xxx
    def index():
        pass
    
    # 第一步:执行 v1 = uuu(9)
    # 第二步:ret = v1(index)
    # 第三步:index = ret 
    @uuu(9)
    def index():
        pass
    
  2. 区别:

    # 普通装饰器
    def wrapper(func):
        def inner(*args,**kwargs):
            data = func(*args,**kwargs) # 执行原函数并获取返回值
            return data
        return inner 
    
    @wrapper
    def index():
        pass
    
    # 带参数装饰器 
    def x(counter):
        def wrapper(func):
            def inner(*args,**kwargs):
                data = func(*args,**kwargs) # 执行原函数并获取返回值
                return data
            return inner 
     return wrapper 
    
    @x(9)
    def index():
        pass
    
  3. 练习题

    # 习题一:
    # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,把每次结果添加到列表中,最终返回列表。
    def xxx(counter):
        def wrapper(func):
            def inner(*args,**kwargs):
                v = []
                for i in range(counter):
                    data = func(*args,**kwargs) # 执行原函数并获取返回值
                    v.append(data)
                return v
            return inner
        return wrapper
    
    @xxx(5)
    def index():
        return 8
    v = index()
    print(v)
    
    # 习题二:
    # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,并返回最后一次执行的结果【面试题】
    def xxx(counter):
        def wrapper(func):
            def inner(*args,**kwargs):
                for i in range(counter):
                    data = func(*args,**kwargs) # 执行原函数并获取返回值
                return data
            return inner
        return wrapper
    
    @xxx(5)
    def index():
        return 8
    v = index()
    print(v)
    
    # 习题三:
    # 写一个带参数的装饰器,实现:参数是多少,被装饰的函数就要执行多少次,并返回执行结果中最大的值。
    def xxx(counter):
        def wrapper(func):
            def inner(*args,**kwargs):
                value = 0
                for i in range(counter):
                    data = func(*args,**kwargs) # 执行原函数并获取返回值
                    if data > value:
                        value = data 
                return value
            return inner
        return wrapper
    
    @xxx(5)
    def index():
        return 8
    v = index()
    print(v)
    

5.10 迭代器

5.10.1 基本知识

  1. 用途:对 某种对象(str/list/tuple/dict/set类创建的对象-可迭代对象)中的元素进行逐一获取

  2. 表象:具有__next__方法且每次调用都获取可迭代对象中的元素(从前到后一个一个获取)

  3. 示例:

    • 列表转换成迭代器:
      • v1 = iter([11,22,33,44])
      • v1 = [11,22,33,44].__iter__()
    • 迭代器想要获取每个值:反复调用 val = v1.__next__()
    v1 = [11,22,33,44]
    
    # 列表转换成迭代器
    v2 = iter(v1)
    
    # 迭代器获取每个值
    result1 = v2.__next__()
    print(result1)
    result2 = v2.__next__()
    print(result2)
    result3 = v2.__next__()
    print(result3)
    result4 = v2.__next__()
    print(result4)
    
    result5 = v2.__next__()
    print(result5)    # 报错:Stoplteration    表示已经迭代结束
    
  4. for循环:运用了迭代器

    v1 = [11,22,33,44]
    
    # 1.内部会将v1转换成迭代器
    # 2.内部反复执行 迭代器.__next__()
    # 3.取完不报错
    for item in v1:
        print(item)
    

5.10.2 可迭代对象

  1. 表象:可以被for循环的对象就可以称为是可迭代对象

  2. 如何让一个对象变成可迭代对象?

    • 在类中实现__iter__方法且返回一个迭代器(生成器)
    # 示例一:
    class Foo:
        def __iter__(self):
            return iter([1,2,3,4])
    
    obj = Foo()
    
    # 示例二:
    class Foo:
        def __iter__(self):
            yield 1
            yield 2
            yield 3
    
    obj = Foo()
    
  3. 注意:只要能被for循环,就是去看他内部的__iter__方法

5.11 生成器

5.11.1 基本知识

  1. 可以理解为:函数的变异、特殊的迭代器、特殊的可迭代对象

  2. 生成器的作用:

    • 生成数据
    • 迭代
  3. 示例:

    # 生成器函数(内部是否包含yield)
    def func():
        print('F1')
        yield 1
        print('F2')
        yield 2
        print('F3')
        yield 100
        print('F4')
    # 函数内部代码不会执行,返回一个 生成器对象 。
    v1 = func()
    # 生成器是可以被for循环,一旦开始循环那么函数内部代码就会开始执行。
    for item in v1:
        print(item)
    

5.11.2 关键字

  1. yield

    • 用途:判断函数是否是生成器函数
  2. yield from

    • 用途:从当前生成器函数跳到其他生成器函数中,执行结束后再回原函数继续执行下面代码
    def base():
        yield 88
        yield 99
    
    def func():
        yield 1
        yield 2
        yield from base()   # 跳到base函数
        yield 3
    
    result = func()
    for item in result:
        print(item)       # 1   2   88   99   3
    

5.11.3 总结

  1. 重点:

    • 函数中如果存在yield,那么该函数就是一个生成器函数
    • 调用生成器函数会返回一个生成器
    • 生成器只有被for循环时,生成器函数内部的代码才会执行,每次循环都会获取yield返回的值
  2. 建议:

    • 生成器函数中一般不要有return

    • 如果需要终止生成器函数中的循环,可以用return

      def func():
          count = 1
          while True:
              yield count
              count += 1
              if count == 100:
                  return
      val = func()
      for item in val:
          print(item)
      
  3. 生成器示例:读取大文件内容

    def func():
        #  分批去读取文件中的内容,将文件的内容返回给调用者。
        cursor = 0
        while True:
            f = open('db', 'r', encoding='utf-8')    # 通过网络连接上redis
            # 代指  redis[0:10]
            f.seek(cursor)
            data_list =[]
            for i in range(10):
                line = f.readline()
                if not line:
                    return
                data_list.append(line)
            cursor = f.tell()
            f.close()  # 关闭与redis的连接
    
            for row in data_list:
                yield row
    
    for item in func():
        print(item)
    

猜你喜欢

转载自www.cnblogs.com/hanfe1/p/11511116.html