Python学习笔记-函数

1.初识函数

# 定义函数
def 函数名():
    '代码'
    ...

# 调用函数
函数名()

函数应用场景:

  • 面向过程编程,从上到下按照业务逻辑功能逐一实现。
  print("xx监控系统")
  
  if cpu使用率 > 90%:
    发送邮件报警
  
  if cpu使用率 > 90%:
    发送邮件报警
  
  if cpu使用率 > 90%:
    发送邮件报警
  • 面向对象编程。(函数式编程)
  def 发送邮件():
    发送邮件
  
  print("xx监控系统")
  
  if cpu使用率 > 90%:
    发送邮件()
  
  if cpu使用率 > 90%:
    发送邮件()
  
  if cpu使用率 > 90%:
    发送邮件()

函数编程的特点和应用场景:

  • 反复使用某个代码段,利用函数【增强代码的重用性】
  • 将代码拆分到不同的函数,【增强代码的可读性】

2.基本使用

def send_email(x1, x2):
    pass

# 位置传参
send_email(123, 22)

# 关键字传参
send_email(x2=123, x1=22)

# 混合使用(关键字在后)
send_email(123, x2=22)

####注意:参数可以是任意类型,也就是说,任意类型的数据都可以做参数传入

  • 参数默认值
    def send_email(x1, x2, x3=999):
      pass
    
    • 参数的默认值中出现了 可变类型 需要谨慎考虑
def func(a1=[]):
  a1.append(2)
  print(a1)

func()      # [2]
func([])    # [2]
func()      # [2, 2]
# 错误分析:执行函数未传递参数,不会每次都重新创建空列表。
  • 动态参数
    • 一个星号:*
        def func(*a1):
            # 会将传进来的参数打包为一个元组
            pass
      
        func(1, 22, 123)  ---> a1 = (1, 22, 123)
        func("xxx", 123)  ---> a1 = ("xxx", 123)
        func([1, 22], 123)  ---> a1 = ( [1, 22], 123 )
        func(1)  ---> a1 = (1,)
        func( (1, 22, 123) )  ---> a1 = ( (1, 22, 123), )
      
    • 两个星号:**
        def func(**a2):
            # 会将传进来的参数打包为一个字典
            pass
      
        func(v1=123, v2=456)  ---> a2 = {"v1": 123, "v2": 456}
        func()  ---> a2 = {}
        func(uu=999)  ---> a2 = {"uu": 999}
      
    • 两种星号混合使用:*,**
        def func(*a1, **a2):
          pass
        
        func(1,2) ---> a1=(1, 2) a2={}
        func(v1=123, v2=456) ---> a1=() a2={"v1": 123, "v2": 456}
        func(99, data=123) ---> a1=(99,) a2={"data": 123}
      
      代表函数是 format函数:def format(self, *args, **kwargs)

    易错点:

     def f1(*args, **kwargs):
         # args=( [111,222,33], )
         # args=( (111,222,33), )
         # kwargs={ "v1": (111,222,33) }
         pass
     
     f1([111,222,33])
     f1((111,222,33))
     f1(v1=(111,222,33))
    
    

函数参数传递方式,是引用还是值?

def func(arg):
    print(arg)

name = "xxx"
func(name)

# 传递的是引用,不会重新拷贝一份数据,再传入到参数中
资料来源:https://zhuanlan.zhihu.com/p/362702418

Python 中参数的传递既不是值传递,也不是引用传递,而是赋值传递,或者是叫对象的引用传递。
需要注意的是,这里的赋值或对象的引用传递,不是指向一个具体的内存地址,而是指向一个具体的对象。
1、如果对象是可变的,当其改变时,所有指向这个对象的变量都会改变。
2、如果对象不可变,简单的赋值只能改变其中一个变量的值,其余变量则不受影响。

3.作用域

作用域,一块共享的区域。

  • python中有一个全局的作用域。
  • 在python中,函数是一个作用域。
    变量的作用域
  • 全局变量和局部变量
  • 全局变量一般是采用大写
  • 全局变量 global关键字
name = "aaa"
def func():
    global name
    name = "bbb"
    print(name)

func()
print(name)

4.函数名也是一个变量名称

  • 函数名有"()", 代表是执行函数,如果没有”()“,则函数名作为一个变量,例如:
def f1():
    pass

f2 = f1()  # 这句代码的意思是将f1函数的执行结果赋值给f2
f3 = f1  # 这行代码意思是将f1函数名赋值给f3,f3将和f1一样拥有相同的函数逻辑,后期可以使用f3
res = f3()  # res是f3执行结果,和res = f1()执行结果相同
# 如何执行这些函数更简便
def send_sms():
    print("发送短信")
def send_email():
    print("发送邮件")
def send_dingding():
    print("发送钉钉")
def send_wechat():
    print("发送微信")

func_list = [send_sms, send_email, send_dingding, send_wechat]
for func in func_list:
    func()

5.匿名函数(lambda表达式)

def func(x, y):
    return 100 + y
func = lambda x,y: 100+y

6.函数进阶

  • 内置函数

    • 第一组
      • open
      f = open("xxx", mode="rb")
      f.read()
      f.close()
      
      • len
      v1 = [111, 22]
      v2 = "abc"
      
      len(v1)
      len(v2)
      
      • range
      v1 = range(5)  # [0,1,2,3,4]
      
      • abs,求绝对值
       v1 = abs(-10)
       print(v1)  # 10
      
      • pow,次方
        pow(2, 5)  # 计算 2 的 5 次方
        # 或者
        v1 = 2 ** 5
      
      • sum,求和
        data = [11, 22, 33]
        res = sum(data)
      
      • divmod, 两个数相除得到商和余数
        v1, v2 = divmod(98, 10)  # v1 = 9, v2 = 8
      
      • round,保留小数后几位
        v1 = round(3.1415926, n)  # 自动四舍五入,保留 n 位小数
      
    • 第二组
      • min,取最小值
        v1 = [11,22,33]
        res = min(v1)  # 11
      
      • max,取最大值
        v1 = [11,22,33]
        res = max(v1)  # 33
      
      • all,检测元素是否全部都是True(类型会转换为bool值)
        v1 = [11,22,33]
        res = all(v1)  # True
        
        v2 = [11,22,"",33]
        res = all(v2)  # False
      
        # 使用场景
        user = input("用户名:")
        pwd = input("密码:")
        
        is_valid = all([user, pwd])
        if is_valid:
          print(user, pwd)
        else:
          print("用户名或密码为空,请重新输入")
      
      • any,只要有True就行
        v1 = [11,22,33,0,""]
        res = any(v1)  # True
        v2 = [0, ""]
        res = any(v2)  # False
      
    • 第三组
      • 进制,计算机底层本质都是二进制,即01代码
        v1 = 90  # 十进制
        v2 = "0b011011"  # 二进制
        v3 = "0o0523"  # 八进制
        v4 = "0x0ab"  # 十六进制
      
      • 十进制和二进制
      十 --> 二
      v1 = bin(90)
      
      二 --> 十
      int("0b011011", base=2)  # base指是几进制
      
      • 十进制和八进制
      十 --> 八
      oct(90)
      
      八 --> 十
      int("0o0523",base=8)
      
      • 十进制和十六进制
      十 --> 十六
      hex(90)
      
      十六 --> 十
      int("0x0ab",base=16)
      
      • 进制转换练习题
          将一个IP地址:ip = "192.168.11.23",将ip地址转换为二进制
          192  --> 0110110
          168  --> 1011010
          11   --> 0110
          23   --> 10110
          
          拼接起来  01101101011010011010110
          转换成十进制数
        
        ip = "192.168.11.23"
        ip_list = list()
        for ip_item in ip.split("."):
            ip_list.append(bin(int(ip_item))[2:])  # 使用[2:]切片的原因是转换为二进制后前缀有“0b”
        # 次数可知,字符串拼接的效率比join的效率低,join的效率高
        print(int("".join(ip_list),base=2))
        
        ip = "192.168.11.23"
        ip_list = list()
        for ip_item in ip.split("."):
            ip_list.append(bin(int(ip_item)))
        # 次数可知,字符串拼接的效率比join的效率低
        print(int("".join(ip_list).replace("0b", ""),base=2))
        
    • 第四组
      unicode是万国码,所有文字和二进制之间对应关系
      • ord
      v1 = ord("A")  # 65
      
      • chr
      v1 = chr(65)  # "A"
      
    • 第五组
      • int
      • str
      • bool
      • list
      • dict
      • tuple
      • float
        v1 = "3.14"
        res = float(v1)  # 3.14
        
      • bytes
        v1 = "abc"
        res = v1.encode("utf8")  # 字节类型
        
        res = bytes("abc", encoding="utf8")  # 与上面相同
        
    • 第六组
      • len
      • print
      • input
      • open
      • range
      • hash
        计算一个值的哈希值(作为建来存储)
      • type
      • callable
        判断是否可执行 --> 函数名
      v1 = "root"
      res = callable(v1)  # False
      
      def v1():
        pass
      
      res = callable(v1)  # True
      
      def func():
        print("我是个函数执行的结果")
      
      data_list = [11,22,55,func]
      for item in data_list:
        if callable(item):
          item()
        else:
          print(item)
      # 11
      # 22
      # 55
      # 我是个函数执行的结果
      
      • enumerate
        循环过程中,自动为你生成一列。
      # 以前生成带序号的方法
      goods = ["手机", "电脑", "小汽车"]
      for item in range(len(goods)):
          print(item, goods[item])
      for item in range(len(goods)):
          print(item+1, goods[item])
      
      goods = ["手机", "电脑", "小汽车"]
      # 使用enumerate函数实现  enumerate(可迭代数据类型,标号起始位置)
      for index, item in enumerate(goods, 1):
          print(index, item)
      # 1 手机
      # 2 电脑
      # 3 小汽车
      
      • sorted
        排序
      可以对可变数据排序
      num_list = [10, 88, 15, 6, 46, 12]
      res1 = sorted(num_list)  # [6, 10, 12, 15, 46, 88]
      res2 = sorted(num_list, reverse=True)  # [88, 46, 15, 12, 10, 6]
      
      可以对不可变数据排序
      num_str = "dhqihdqd"
      res1 = sorted(num_list)
      res2 = sorted(num_list, reverse=True)  # ['q', 'q', 'i', 'h', 'h', 'd', 'd', 'd']
      
      sorted函数可以通过key指定排序的规则,接受的是一个函数的返回值作为排序依据
      # 新问题:如果有需求,通过序号进行排序
      num_list = ["1.python基础.mp4", "2.环境搭建.mp4", "3.pycharm安装.mp4", "10.总结"]
      # 正常排序后
      print(sorted(num_list)) # ['1.python基础.mp4', '10.总结', '2.环境搭建.mp4', '3.pycharm安装.mp4']
      
      # sorted函数可以指定排序规则 sorted(num_list, key=接受的是一个函数的返回值) 会按照接受的函数返回的值作为排序依据进行排序
      def func(ele):
          return int(ele.split(".")[0])
      
      print(sorted(num_list, key=func)) # ['1.python基础.mp4', '2.环境搭建.mp4', '3.pycharm安装.mp4', '10.总结']
      # 可以将上述代码简化,使用lambda函数
      print(sorted(num_list, key=lambda ele:int(ele.split(".")[0]))) # ['1.python基础.mp4', '2.环境搭建.mp4', '3.pycharm安装.mp4', '10.总结']
      

      注意

      sorted函数是将原有数据进行排序后生成一个新的列表,不论是可变或者不可变数据,经过排序后返回的都是一个列表
      
      提出问题:sorted函数可以生成排序后的新列表,那么列表的sort函数呢,是新生成一个列表还是将原有列表进行排序?
      num_list = [10, 88, 15, 6, 46, 12]
      
      print("num_list使用sorted函数前的结果为{}".format(num_list)) # [10, 88, 15, 6, 46, 12]
      res1 = sorted(num_list)  # [6, 10, 12, 15, 46, 88]
      print("num_list使用sorted函数后的结果为{}".format(num_list)) # [10, 88, 15, 6, 46, 12]
      
      print("num_list使用列表的.sort()方法前的结果为{}".format(num_list)) # [10, 88, 15, 6, 46, 12]
      res2 = num_list.sort()  # res2 值为None,说明sort方法是在原有的列表上进行排序,没有返回值
      print("num_list使用列表的.sort()方法后的结果为{}".format(num_list)) # [6, 10, 12, 15, 46, 88]
      
  • 生成器

    定义函数时,出现yield关键字,此函数就是一个生成器函数

    def func():
      print("abc")
    
      yield 1
      yield 2
      yield 3
    
    # 执行生成器函数,会返回一个生成器对象
    obj = func()  # obj是一个生成器对象
    
    # 生成器对象.__next__(),就会进入函数去执行,一旦遇到了yield,会跳出函数
    obj.__next__()
    

    生成器有什么用?
    需求:创建1000W个数字

    方案1:
    在内存中先创建列表[0,1,2,3...],再循环输出。
    会占用大量内存,不实际
    
    方案2:
    不真创建,代码逻辑后续逐一生成数字,循环获取
    def creat_num(total):
      num = 0
      while num < total:
          yield num
          num += 1
    
    res = creat_num(100)  # 返回的是一个生成器对象
    for item in res:
       print(item)
    

    理解:生成器到底是怎么回事?

    执行过程 + 优势
    
    在python2中,
    range()函数是在内存中生成一系列数 -- 速度较慢
    xrange()函数是类似于生成器的方法实现 -- 速度快
    
    在python3中,在python2的基础上,升级为
    range()函数使用的也是类似于生成器的方法,边调用,边生成
    
  • 高阶函数,闭包

    name = "root"
    
    def func():
      v1 = 123
      def inner():
          print(666)
      
      return inner
    
    res = func()
    print(666)
    
    res()  # 只有当调用这个函数时,才会执行
    

    闭包,通过函数嵌套的机制,先将数据封装到作用域(包),后续再执行内部函数时,再去获取之前封装进去的值即可。

  • 结合线程池 + 闭包完成批量下载的一个使用案例

    import time
    from concurrent.futures import ThreadPoolExecutor
    import requests
    
    # 要干的活
    def task(url):
      # 下载
      res = requests.get(url)
      return res.content
    
    def outer(file_name):
      def done(response):
          """将下载的内容写到文件,需要知道下载的那个文件名"""
          with open(file_name, mode="wb") as f:
              f.write(response.result())
      return done
    
    # def test(response):
    #     print("test,{}".format(response))
    
    # 线程池中最多创建5个线程
    pool = ThreadPoolExecutor(5)
    
    img_list = [
      {"file_name": "比亚迪.jpg", "url": "https://car3.autoimg.cn/cardfs/product/g26/M06/89/23/380x285_0_q87_autohomecar__CjIFVmQ2i1mAF0lRACGN6qouWmA140.jpg"},
      {"file_name": "朗逸.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g27/M03/D3/67/380x285_0_q87_autohomecar__ChxkmWLmMI2ACHSvABBW_v_Ebv0632.jpg"},
      {"file_name": "雷凌.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g26/M02/94/F0/380x285_0_q87_autohomecar__ChxkjmQbHTWAfiu1AA2Qc45KmBk129.jpg"},
      {"file_name": "比亚迪2.jpg", "url": "https://car3.autoimg.cn/cardfs/product/g26/M06/89/23/380x285_0_q87_autohomecar__CjIFVmQ2i1mAF0lRACGN6qouWmA140.jpg"},
      {"file_name": "朗逸2.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g27/M03/D3/67/380x285_0_q87_autohomecar__ChxkmWLmMI2ACHSvABBW_v_Ebv0632.jpg"},
      {"file_name": "雷凌2.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g26/M02/94/F0/380x285_0_q87_autohomecar__ChxkjmQbHTWAfiu1AA2Qc45KmBk129.jpg"},
      {"file_name": "比亚迪3.jpg", "url": "https://car3.autoimg.cn/cardfs/product/g26/M06/89/23/380x285_0_q87_autohomecar__CjIFVmQ2i1mAF0lRACGN6qouWmA140.jpg"},
      {"file_name": "朗逸3.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g27/M03/D3/67/380x285_0_q87_autohomecar__ChxkmWLmMI2ACHSvABBW_v_Ebv0632.jpg"},
      {"file_name": "雷凌3.jpg", "url": "https://car2.autoimg.cn/cardfs/product/g26/M02/94/F0/380x285_0_q87_autohomecar__ChxkjmQbHTWAfiu1AA2Qc45KmBk129.jpg"},
    ]
    
    # 10个任务
    for item in img_list:
      fur = pool.submit(task, item.get("url"))
      fur.add_done_callback(outer(item.get("file_name")))
      # fur.add_done_callback(test)  # 分析可得,add_done_callback()所需的参数是函数名,会将传入的函数名进行执行
    
    
  • 装饰器

    def func():
      print("我是func函数")
      return "中国万岁"
    
    def outer(func):
      def inner():
          print("before")
          result = func()
          print("after")
          return result
      return inner
    
    func = outer(func)
    res = func()
    

    装饰器优势:不修改原函数内容的前提下,进行了功能的扩展
    Python中的装饰器,本质上就是利用:

    • 嵌套 + 闭包
    • @xx语法糖
      构建出来的一个功能,满足在不修改原函数内部代码的前提下,可以在函数执行前和后扩展我们自定义的功能。
    # 装饰器一般格式
    def outer(func):
      def inner():
          # 扩展功能
          res = func()
          return res
      return inner
    
    @outer
    def func():
      print(123)
      return 999
    
  • 推导式(不属于函数,额外内容)
    用更少的代码,实现某个特定的功能

  • 需求:创建一个列表,列表 = ["用户-0", "用户-1", "用户-3"...50个]
    

    不使用推导式的常规方法

    data_list = []
    for i in range(51):
      data_list.append("用户-{}".fromat(i))
    print(data_list)
    
  • 列表推导式

    data_list = [ i for i in range(51) ]  # [0,1,2,3,4,5,6,...]
    
    data_list = [ 100 for i in range(51) ]  # [100,100,100,100...]
    

    使用列表推导式完成上述需求

    data_list = [ "用户-{}".format(i) for i in range(51) ] 
    

    列表推导式添加条件

    data_list = [ i for i in range(51) if i > 9]
    
    file_list = ["day01_python基础.mp4","day02_环境搭建.mp4","day03_pycharm安装.mp4","day10_总结","a1.png"]
    # 将day开头的文件提取到新的列表
    result = [item for item in file_list if item.startswith("day")]
    
    # 将以mp4为结尾的文件提取出来
    result = [item for item in file_list if item.endswith("mp4")]
    
    练习:使用列表推导式,生成一个列表,下标位置的数值为此下标之前的总和,可以使用sum函数
    # 要求
    ans = [0, 1, 3, 6, 10]
    #      0  1  2  3   4
    
    # 分析过程
    # result = []
    # for i in range(10):
    #     result.append(sum(range(0,i+1)))
    
    # 答案
    result = [sum(range(0, i+1)) for i in range(10)]
    print(result)  # [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
    
  • 字典推导式

     info = {i:123 for i in range(10)}  # {1:123,2:123,3:123...}
    
     info = {i:123 for i in range(10) if i > 5}  # {6:123,7:123,8:123...}
    
    • 练习题

      1.给一个列表,将列表中的每一个元素尾部.mp4剔除。

      data_list = [
      '1-5 编译器和解释器.mp4',
      '1-17 今日作业.mp4',
      '1-9 Python解释器种类.mp4',
      '1-16 今日总结.mp4',
      '1-2 课堂笔记的创建.mp4',
      '1-15 Pycharm使用和破解(win系统).mp4',
      '1-12 python解释器的安装(mac系统).mp4',
      '1-13 python解释器的安装(win系统).mp4',
      '1-8 Python介绍.mp4',
      '1-7 编程语言的分类.mp4',
      '1-3 常见计算机基本概念.mp4',
      '1-14 Pycharm使用和破解(mac系统).mp4',
      '1-10 CPython解释器版本.mp4',
      '1-1 今日概要.mp4',
      '1-6 学习编程本质上的三件事.mp4',
      '1-18 作业答案和讲解.mp4',
      '1-4 编程语言.mp4',
      '1-11 环境搭建说明.mp3']
      
      file_list = [item.replace(".mp4","") for item in data_list]  # 将mp4替换
      file_list = [item.replace(".mp4","") for item in data_list if item.endswith(".mp4")]  # 将mp4替换
      file_list = [item.rsplit(".", maxsplit=1)[0] for item in data_list]  # 切分,如果文件中后缀不止有.mp4,则会出现问题
      file_list = [item.rsplit(".", maxsplit=1)[0] for item in data_list if item.endswith(".mp4")]  # 切分, 添加判断,解决上述问题
      

      2.有一段字符串,将字符串改变为字典格式

      data_string = "name=吴佩琦&email=xxx@live.com&gender=男"
      # 拆分为字典
      """
      info = {
        "name": "吴佩琦",
        "email": "xxx@live.com",
        "gender": "男"
      }
      """
      info = {item.split("=")[0]: item.split("=")[1] for item in data_string.split("&")}
      

      3.有一个字典,构成一个字符串,将上面的题逆转过来

      result = "&".join(["{}={}".format(k,v) for k, v in info.items()])
      

      4.微信支付,微信对API授权,加密校验过程

      info = {
      'sign_type': "MD5",
      'out_refund_no': "12323",
      'appid': 'wx55cca0b94f723dc7',
      'mch_id': '1526049054',
      'put_trade_no': "ffff",
      'nonce_str': "sdfdffd",
      'total_fee': 9901,
      'refund_fee': 10000
      }
      # 要求:根据键从小到大排序,最终获取如上题的字符串
      
      result = "&".join(["{}={}".format(k, info.get(k)) for k in sorted(info.keys())])
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

希望有所成就的李桑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值