• guxh的python笔记二:函数基础


    1,函数的参数

    1.1,查看函数的参数类型

    def run(a, *args, b, **kwargs):
        return a + b
    

    可以通过如下方式查看参数类型:

    import inspect
    k = inspect.signature(run)
    for i, j in k.parameters.items():
       print('{:7}:'.format(i) , j.kind)

    输出结果为:

    a        : POSITIONAL_OR_KEYWORD
    args     : VAR_POSITIONAL
    b        : KEYWORD_ONLY
    kwargs   : VAR_KEYWORD
    

    含义如下:

    POSITIONAL_OR_KEYWORD:位置参数或者关键字参数,可以用位置参数或者关键字参数方式传参

    VAR_POSITIONAL:可变位置参数,*args

    KEYWORD_ONLY:仅限关键字参数,*args或者*后面的,只能用关键字参数方式传参

    VAR_KEYWORD:可变关键字参数,**kwargs

    POSITIONAL_ONLY:目前不支持

    备注:

    *args是最后一个位置参数,即*args后面的都是“仅限关键字参数” 

    **kwargs是最后一个参数,即**kwargs后面不允许再跟参数

    1.2,传参方法

    位置参数必须在关键字参数前面!

    def test(x, y,z)
        .......

    传参:

    位置参数:test(a, b, c)  
    关键字接收:test(y=a, x=b, z=c)   # 关键字参数,可颠倒次序
    混合:test(1, z=2, y=6) # 位置参数必须在关键字参数前面,后面2个可以颠倒
    

    1.3,*args,数量可变位置参数

    args:序列

    *args:  序列中的值

    def test(x, *args):
        print(x, *args)  # 1,2,3
        print(x, args)  # 1,(2,3)
    test(1,2,3) # 或test(*[1,2,3])

      

    1.4,**kwargs,数量可变的关键字参数

    kwargs:字典

    *kwargs: 字典的key

    **kwargs:字典的键值对

    def test(**kwargs):
        print(kwargs)   #  {'name': 'guxh', 'age': 18}
        print(*kwargs)   # name age
        print(kwargs['name'])  # 'guxh'
    test(name='guxh', age=18) # 或test(**{'name':'guxh', 'age':18})

    1.5,只接受仅限关键字参数

    *后面的是仅限关键字参数:

    def test(x, *, y):
    	print(x, y)
    
    test('a', 'b')  # TypeError
    test('a', y='b')  # OK
    

    *args后面的是仅限关键字参数:

    def test(*args, y):
    	print(*args, y)
    
    test('a', 'b')  # TypeError
    test('a', y='b')  # OK
    

      

    1.6,特殊传参

    组合传参,如果name用关键字参数传参,则age必须用关键字参数传参,因为位置参数必须在关键字参数前面。

    def test(name, age=18, *args, **kwargs):
        print(name)  # 位置参数或关键字参数,'guxh'
        print(age)   # 位置参数或关键字参数,34
        print(args)   # 可变位置参数,()
        print(kwargs)  # 可变关键字参数,{'sex': 'f', 'hobby': 'python'}
    test('guxh', 34, sex='f',hobby='python')
    test(name='guxh', age=34, sex='f',hobby='python')

    序列传参,字典传参:

    def test(x, y):test(*序列),或test(**字典)

    def test(*args) : test(*序列)

    def test(**kwargs) :test(**字典)

    2,防御可变参数

    python的传参是按引用传参,但是函数对传入的参数进行修改以后:

    可变参数:例如列表/字典,会就地修改,实际相当于传统的"按引用传参"

    不可变参数:例如数值/字符串/远组,会重新开辟内存,结果相当于传统的"按值传参"

    2.1,不可变参数传参

    函数或类,在改变x之前,x的id与实参a的id完全一样,但是改变后就不一样了,也就是x发生改变时不会影响到实参a,效果相当于传统的按值传参。

    函数传参:

    def run(x):
        print(id(x))  # 1591569856
        x += 1
        print(id(x))  # 1591569888
    
    a = 0
    run(a)
    print(a, id(a))  # 0, 1591569856
    

    类的实例化传参:

    class Foo:
    
        def __init__(self, x):
            self.x = x
            print(id(self.x))   # 1591569856
            self.x = self.x + 1
            print(id(self.x))   # 1591569888
    
    a = 0 
    print(id(a))  # 1591569856
    f = Foo(a)
    

    2.2,可变参数

    函数或类,对x的改变会影响到实参a,效果相当于传统的按引用传参。

    函数传参:

    def run(x):
        x.append(1)
    
    a = []  
    run(a)
    print(a)  # [1]
    

     类的实例化传参:

    class Foo:
    
        def __init__(self, x):
            self.x = x
            self.x.append(1)
    
    
    a = []
    f = Foo(a)
    print(a)   # [1]
    

     因此如果不想改变原来的a,可以用浅拷贝或深拷贝:

    class Foo:
    
        def __init__(self, x):
            self.x = list(x)
    

     2.3,可变参数作为默认值的问题

    如果想实现对一个输入列表添加一个1,当没有输入列表时,创建一个空列表并添加1,会发生如下问题:

    函数:

    def run(x=[]):
        x.append(1)
        return x
    
    a = [1, 2, 3]
    print(run(a))  # [1, 2, 3, 1],有参数时表现正常
    print(run())   # [1],第一次run也正常
    print(run())   # [1, 1],第二次run出错了,返回的是有2个1的列表
    

     类的实例化传参:

    class Foo:
    
        def __init__(self, x=[]):
            self.x = x
            self.x.append(1)
    

     根据不同打印顺序,结果不一样:

    f1 = Foo()  
    f2 = Foo()
    print(f1.x)  # [1, 1]
    print(f2.x)  # [1, 1]
    
    f1 = Foo()
    print(f1.x)  # [1]
    f2 = Foo()
    print(f2.x)  # [1, 1]
    

    实际上f1和f2实例共享了同一个列表,所以都完成实例化后打印的值完全一样。

    解决方法是函数或类有可变参数作为默认值时用None:

    def run(x=None, y=None):
        x = [] if x is None else x 
        y = {} if y is None else y  # requests框架使用了这2句
        x.append(1)
        y.update({'a': 1})
        return x, y
    

    3,高阶函数

    3.1,高阶函数

    满足下面两个条件之一的就叫高阶函数:

    1)一个函数当作参数传给另外一个函数(参数有函数)

    2)函数的返回值中包含函数(返回有函数)

    3.2,回调函数

    回调本质上就是高阶函数的第一个定义场景。

    函数A作为参数传给另外一个函数B,涉及三个角色:中间函数A,回调函数B,中间函数A的调用者(一般是主程序)。

    如果回调函数想保存额外状态,可以用基于类、闭包、协程、functools.partial、lambda等方式实现。

    简单的回调函数:

    def add(x, y):              # 回调函数
        print(x + y)   
    
    def fun(x, y, callback):    # 中间函数
        return callback(x, y)
    
    if __name__ == '__main__':  # 中间函数调用者
        fun(1, 2, callback=add)  
    

    保存额外状态的回调 — 基于类:

    class Foo:
        def __init__(self):
            self.s = 0  # 额外状态s      
        def add(self, x, y):
            self.s += 1
            print('第{}次相加结果:{}'.format(self.s, x + y))
    
    def fun(x, y, callback):
        return callback(x, y)
    
    if __name__ == '__main__':     # 中间函数调用者
        f = Foo()
        fun(1, 2, callback=f.add)  # 第1次相加结果:3
        fun(3, 4, callback=f.add)  # 第2次相加结果:7
    

    保存额外状态的回调 — 基于类 + functools.partial / lambda:

    class Seq:         # 保存额外状态的类
        def __init__(self):
            self.seq = 0
    
    def fun(x, y, callback):
        return callback(x, y)
    
    def add(x, y, s):
        s.seq += 1
        print('第{}次相加结果:{}'.format(s.seq, x + y))
    
    if __name__ == '__main__': 
        s = Seq()
        fun(1, 2, callback=partial(add, s=s)) 
        fun(3, 4, callback=partial(add, s=s)) 
        # 用lambda替代functools.partial 
        # fun(1, 2, callback=lambda x, y: add(x, y, s))  
        # fun(3, 4, callback=lambda x, y: add(x, y, s))  
    

    保存额外状态的回调 — 基于闭包:

    def make_add():
        s = 0
        def add(x, y):
            nonlocal s
            s += 1
            print('第{}次相加结果:{}'.format(s, x + y))
        return add
    
    def fun(x, y, callback): 
        return callback(x, y)
    
    if __name__ == '__main__':  
        add = make_add() 
        fun(1, 2, callback=add) 
        fun(3, 4, callback=add) 
    

    保存额外状态的回调 — 基于协程:

    def make_add():
        s = 0
        while True:
            x, y = yield     # 不产出值,只接收值(接收值在yield左边,产出值在yield右边)
            s += 1
            print('第{}次相加结果:{}'.format(s, x + y))
    
    def fun(x, y, callback):
        return callback((x, y))   # 协程只能接受一个参数,因此改为发送元组
    
    if __name__ == '__main__': 
        add = make_add()
        next(add)
        fun(1, 2, callback=add.send)  # 往协程发送数据是send
        fun(3, 4, callback=add.send)  
    

    4,常用内置函数

    4.1,匿名函数lambda

    调用方法:

    >>> fn = lambda x:x*3
    >>> fn(3)
    9
    >>> (lambda x:x*3)(3) 
    9

    lambda y: x + y,其中输入参数y可变,x在运行时才绑定,注意以下代码的区别:

    funs1 = [lambda x: x + n for n in range(5)]
    for f in funs1:
        print(f(0), end=' ')  # 4 4 4 4 4
    funs2 = [lambda x, n=n: x + n for n in range(5)] for f in funs2: print(f(0), end=' ') # 0 1 2 3 4

    4.2,计算

    1)sum:求和,归约函数,归约函数可以直接结合生成器表达式,避免创建临时列表

    nums = [1, 2, 3, 4, 5]
    s = sum(x*x for x in nums)   # 55,如果nums很大,用列表推导会有很大开销
    

    2)max:最大值,归约函数

    3)min:最小值,归约函数

    4)round:保留小数位数

    5)divmond:相除,返回商和余

    6)eval

    7)exec

    8)hash:哈希

    9)id:获取ID

    10)int / float / hex / oct / bytes / char / bin / chr / ord : 类型转换

    11)pow:次方

    4.3,检测

    1)all:全部为真返回True,否则返回False,归约函数

    2)any:任意为真返回Ture,否则返回False,归约函数

    3)bool:判断True or False

    4)callable:判断是否可调用

    5)isinstance:判断类

    6)issubclass:判断子类

    7)type

    class Human:
        pass
    
    class Male(Human):
        pass
    
    m = Male()
    print(isinstance(m, Human))     # True
    print(isinstance(m, Male))      # True
    print(issubclass(Male, Human))  # True
    print(type(m))                  # <class '__main__.Male'>,如果是import,__main__显示module名
    

    4.4,面向对象

    1)super:继承父类

    2)vars:返回所有属性名,相当于self.__dict__?

    3)setattr / delattr:设置属性 / 删除属性

    4)dir:看object部分方法

    4.5,迭代

    1)iter:获取iterator,iter()可以加哨符值取代while

    2)zip:两个序列合起来

    3)enumerate:获取index和value

    4)map:返回iterator

    m = map(lambda x: x * 2,  [1, 2, 3])
    print(list(m))
    print(dir(m))    # 含有'__iter__', '__new__'
    print(type(m))   # <class 'map'>
    print(type(iter(m)))   # <class 'map'>,iterator的__iter__是return self
    

    5)filter:返回iterator

    print(list(filter(lambda x: x > 1, [1, 2, 3])))   # [2, 3]
    print(list(filter(None, ['', None, 1, 'a'])))     # [1, 'a']
    

    6)range:返回iterable

    r = range(10)
    print(dir(r))    # 含__iter__,但不含__next__
    print(type(r))   # <class 'range'>
    print(type(iter(r)))   # <class 'range_iterator'>,iterable的__iter__是return iterator
    

    4.6,数据类型

    1)tupple,list,dict,set,frozenset:转换为元组/列表/字典/集合/不可变集合

    2)slice:切片

    3)sort / sorted:排序

    4)reversed:反向

    4.7,文本

    1)str

    2)repr

    3)replace:替换

    4)strip:去除空格

    5)format

    name = 'guxh'
    age = 20
    print("my name is " + name + ", age is " + str(age))
    print("my name is %s, age is %s" % (name, age))
    print("my name is {}, age is {}".format(name, age))
    print("my name is {0}, age is {1}".format(name, age))
    print("my name is {name}, age is {age}".format(name=name, age=age))
    print("my name is {name}, age is {age}".format(**{'name': name, 'age': age}))
    

    格式化参数:

    ^, <, > 分别是居中、左对齐、右对齐,后面带宽度,分别等效于center, ljust, rjust。

    : 号后面带填充的字符,只能是一个字符,不指定则默认是用空格填充。

    + 表示在正数前显示 +,负数前显示 -;  (空格)表示在正数前加空格

    b、d、o、x 分别是二进制、十进制、八进制、十六进制

    eg:>右对齐,@缺失部分用@填充,18固定宽度,','千分位,'.2'浮点精度2,f浮点数声明

    '{:@>18,.2f}'.format(70305084.0)   # 输出@@@@@70,305,084.00
    

    6)文本对齐

    >>>text = 'hello world'
    >>>text.center(20, '*')  # 等效format(text, '*^20s'), '{:*^20s}'.format(text)
    '****hello world*****'
    >>>text.ljust(20, '*')   # 等效format(text, '*>20s'), '{:*>20s}'.format(text)
    'hello world*********'
    >>>text.rjust(20, '*')   # 等效format(text, '*<20s'), '{:*<20s}'.format(text)
    '*********hello world'
    

      

    4.8,显示交互

    1)print:打印,end指定结束换行符

    >>> for i in range(5):
    ...     print(i, end='  ')
    0 1 2 3 4  

    读取文件时有默认的换行符号,加上print自动产生的换行符,就会产生空行,可以用end去除空行:

    with open('text.txt') as f:
        for line in f:
            print(line, end='')

    sep指定间隔,join()只能合并同类型,print+sep可以合并不同类型

    >>>print(*list('abcd'), sep='#') 
    a#b#c#d

    2)保存内容到f文件

    print('hello world', file=f)
    

    4.9,文件IO

    打开文件:

    open()函数返回的是个iterator

    >>>from collections import abc
    >>>f = open('text.txt')
    >>>isinstance(f, abc.Iterator)
    True
    

       

    参数说明:

    • t:文本,默认
    • r :读,默认
    • w:写,有同名文件覆盖,无同名文件新建
    • a :追加,有同名文件追加,无同名文件新建
    • r+ :  读写(读模式 +写模式/追加模式),写入时和指针指向第几行没有关系,python3总是追加到最后面
    • w+ :没什么用!写读(先创建文件再往里面写),文件不存在会创建,存在就覆盖,和w一样比较危险
    • a+:  比较常用,追加模式 + 读模式,即附加了读的a追加模式(相当于r+a),可以读,也可以追加到最后
    • rb,wb,ab :二进制的基本操作,python3里以rb打开以后无需encode到bytes类型
    • rb+, wb+, ab+ : 二进制的读写操作
    • x:文件不存在时才写入,可以省略os.path.exists判断
    • encoding:编码
    • newline:python会自动将系统换行符(unix是 ,windows是 )转换成 ,设置newline=‘’可以取消自动转换

      

    常用方法:

    f:是iteraotr,可以逐行打印,但无法获取下标

    for i in f: 
        print(i)

    f.read() :一次性全部读取所有内容,注意只能读一次,读第二次会为空

    f.readline()  :逐行读取,读5行可以:

    for i in range(5):  
        print(f.readline())
    

    f.readlines()  :逐行读取所有内容,可以打印所有,f.readlines()和list(f)结果是一样的

    for i in f.readlines() : 
        print(i)
    

    f.tell():返回文件句柄当前指针指向的位置,指针位置是根据字符串确定的,不是根据行数确定的。

    f.seek():返回到指针位置,例如f.seek(0),回到指针0的位置,f.seek()一般配合f.tell()使用,f.tell()确定指针位置,f.seek()回到指针位置。

    f.encoding:获取文件编码

    f.name():获取文件名

    f.buffer

    f.fileno():打印文件句柄在内存中的位置

    f.isatty():判断是否是一个终端设备

    f.seekable():判断文件是否可以移回去

    f.writeable():判断文件是否可写

    f.flush():刷新,写一句语句后不一定马上写到硬盘上,实际是等积满一定内容后再写到硬盘上,flush可以实时刷到硬盘上

    # CMD写文件刷新
    f = open('test.txt', 'w')
    f.write('hello world 1 
    ')  #此时去打开文件发现还没有写入
    f.flush()  # 此时去打开发现已写入
    f.close()
    # 打印进度条:
    import(sys)
    import(time)
    for i in range(50):
        sys.stdout.write('#')
        sys.stdout.flush()
        time.sleep(0.1)
    

    f.truncate():不写参数就清空

    f.truncate(10)   # 从开头截断至10
    f.seek(10);   # 从第10个字符开始,往后截断至10
    f.truncate(10)   
    

    f.writelines(s):s是个iterable,相当于:

    for i in s:
        f.write(i)
    

    f.readinto(buff):将二进制数读取到可变缓冲区中

      

    修改文件内容

    • 方法一,读到内存里修改
    • 方法二,修改内容,写到新文件中

    读写压缩文件

    • gzip.open:gz
    • bz2.open:bz2

    4.10,其他函数

    1)globals:返回所有key,value

    5,其他

    5.1,函数注释

    可以对函数的参数类型,返回类型,做注释:

    def fun(x: int, y: int) -> int:
        return x + y
    

    5.2,exec和eval

    exec:执行复杂的python代码,无返回结果(相当于JavaScript中的eval)

    eval:计算单个表达式的值,有返回结果

  • 相关阅读:
    usb驱动之打印usb设备信息(一)
    驱动的分离分层设计——按键点灯
    三极管(7)之开关电路详解
    电阻(4)之上拉电阻与下拉电阻详解
    电感的能量储存在哪里-史上最深度的解析(6)
    电感的能量储存在哪里-深度解析(4)
    电容(2)旁路电容工作原理深度解析
    555定时器(1)单稳态触发器电路及Multisim实例仿真
    N沟通场效应管深度图解(1)工作原理及Multisim实例仿真
    开关电源(1)之BUCK降压变换器工作原理及Multisim实例仿真
  • 原文地址:https://www.cnblogs.com/guxh/p/10252557.html
Copyright © 2020-2023  润新知