Python函数对象

python有以下几种可调用对象:

  • 用户自定义函数
  • Lambda表达式创建的匿名函数
  • 内置函数
  • 内置方法
  • 类方法
  • 类,主要是类的魔法方法,如构建函数、析构函数等
  • 函数对象,可调用类实例
  • 生成器

本文主要学习函数对象。

函数对象概念

在Python中一切皆对象,函数也是一种对象,有相关的属性和方法。对于任意对象,我们可以用dir()函数来获取其内置的属性及方法名,例如:

def add(a: int, b: int=1) -> int:
    """加法函数"""
    return a + b

f = add #函数对象作为变量进行赋值
print(dir(f))

‘’'
['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__getstate__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

‘''

函数可以做什么?

  1. 函数可以作为变量来使用。
  2. 函数可以作为参数传入另外一个函数。
  3. 函数可以作为返回值作为结果被另一个函数返回。
  4. 函数可以作为容器的元素。

 函数作为变量

可以将函数作为变量一样使用

def func():
    print('func() run')

# 注意func和func()的区别?
#   func是变量名,相当于内存地址的别名。
#   func()是调用函数,得到的是一个结果。

# 1、可以赋值:函数可以被引用
func
f = func  # 就是将func的内存地址赋给一个变量
print('f,func的对象地址:', f,func)  # f、func指向同一个对象, 
f1 = func()  # func运行,并将func()的结果赋给一个变量f1
print('func()的运行结果:', f1)  # None
print('func运行,并将结果返回:', f())  # func run    None
print('func运行,并将结果返回:', func())  # func run    None

‘’'
f,func的对象地址:  
func() run
func()的运行结果: None
func() run
func运行,并将结果返回: None
func() run
func运行,并将结果返回: None
‘''

函数作为参数

可以当作函数的参数传入
def foo(x):   # x = func的内存地址
    print('foo() run :', x)  # 
    if callable(x) :
        x()

a = 111
foo(111) #foo() run : 111
foo(a)   #foo() run : 111
# func的内存地址当作参数传入
foo(func)  # foo() run : 

‘’’
foo() run : 111
foo() run : 111
foo() run : 
func() run
’‘’

函数作为返回值

可以把作函数当作另一个函数的返回值
def foo(x):  # x = func的内存地址
    return x  # return func的内存地址

print('func:', func) #func: 
res = foo(func)
print('res:', res) #通过foo()函数的返回值,得到了func()的地址,res: 

res()  # 加入()后得到结果:func run

函数作为容器元素

 函数对象作为不可变对象,可以作为容器元素

def func1():
    print('func1() run')

def func2():
    pass

def func3():
    pass

l1 = [func1, func2, func3, 'end']
print(l1)
#[, , , 'end']
t1 = (func1, func2, 'end')
print(t1)
#(, , 'end')

def func1():
    print('func1() run')

def func2():
    pass

def func3():
    pass

l1 = [func1, func2, func3, 'end']
print(l1)
#[, , , 'end']
l1[0]()
t1 = (func1, func2, 'end')
print(t1)
#(, , 'end')
t1[0]()
d1 = {1:func1, 2:func2, 3:func3}
print(d1)
d1[1]()
d2 = {func1:1, func2:2, func3:3}
print(f'{d2=}’)

‘’'
[, , , 'end']
func1() run
(, , 'end')
func1() run
{1: , 2: , 3: }
func1() run
d2={: 1, : 2, : 3}
‘''

函数对象常见属性

函数常见属性表

属性 返回类型 说明
__name__ str 函数名
__qualname__ str 函数限定名,例如,函数在一个类中时,其限定名为 类名.函数名
__doc__ str or None 函数注释(docstring)
__annotations__ dict or None 函数函数参数及返回值类型注释
__module__ str 函数所属模块名
__class__ 类对象 函数所属类(对象)
__code__ code对象 函数代码(对象)
__defaults__ tuple or None 函数参数默认值
__kwdefaults__ tuple or None 函数限定关键字参数默认值
__closure__ tuple of cell or None 闭包函数中引用的cell变量

 基本属性举例

def add(a: int, b: int=1) -> int:
    """加法函数"""
    return a + b

print('函数名:', add.__name__)
print('函数限定名:', add.__qualname__)
print('函数注释(docstring):', add.__doc__)
print('函数参数及返回值类型注释:', add.__annotations__)
print('函数所属类(对象):', add.__class__)
print('函数所属模块名:', add.__module__)
print('函数参数默认值:', add.__defaults__)

‘’’
函数名: add
函数限定名: add
函数注释(docstring): 加法函数
函数参数及返回值类型注释: {'a': , 'b': , 'return': }
函数所属类(对象): 
函数所属模块名: __main__
函数参数默认值: (1,)
’‘’

函数名及函数限定名

class Calc:
    def add(self, a, b):
        return a + b
print('函数名:', Calc.add.__name__)
print('函数限定名:', Calc.add.__qualname__)

‘’’
函数名: add
函数限定名: Calc.add
’‘’

关键字限定参数默认值

def add(*, a, b=1):
    return a + b

# add(1,2)  # 报错,不允许按位置参数形式使用
# add(a=1, b=2) # 可用
print('函数限定关键值参数默认值:', add.__kwdefaults__)

‘’’
函数限定关键值参数默认值: {'b': 1}
’‘’

闭包函数获取自由变量列表

def calc(method, a, b):
    def add():
        return a + b

    if method == 'add':
        return add
    # ...

add = calc('add', 1, 2)  # 得到闭包函数add
print('回调函数中自由变量列表', add.__closure__)
for cell in add.__closure__:
    print('自由变量值:', cell.cell_contents)

‘’’
回调函数中自由变量列表 (, )
自由变量值: 1
自由变量值: 2
’‘’

函数代码对象

函数的__code__属性返回一个code(Python字节码)对象。

代码对象常见属性表

代码对象常见属性如下:

属性 返回类型 说明
co_filename str 代码所在文件路径
co_firstlineno int 代码第一行行号
co_argcount int 常规参数数量
co_posonlyargcount int 限定位置参数数量
co_kwonlyargcount int 限定关键字参数数量
co_nlocals int 局部变量数量
co_varnames tuple 局部变量名列表
co_freevars tuple 自由变量名列表
co_cellvars tuple cell变量(多作用域变量)名列表
co_names tuple 引用其他函数名列表
co_consts tuple 所使用常量列表,包含函数的docstring、内部函数等
co_code bytes 编译后的二进制操作码(opcodes)
co_lnotab bytes 地址及代码行号映射编码后的二进制
co_flags int 操作标记值
co_stacksize int 使用的栈大小

 基本属性举例

def add(a, b):
    """加法函数"""
    s = a + b
    return s

code = add.__code__

print('函数代码所在文件路径:', code.co_filename)
print('函数代码第一行行号:', code.co_firstlineno)

print('参数数量:', code.co_argcount)
print('关键字限定参数数量:', code.co_kwonlyargcount)
print('位置限定参数数量:',code.co_posonlyargcount)
print('局部变量数量:', code.co_nlocals)

print('局部变量名列表:', code.co_varnames)
print('cell变量名列表:', code.co_cellvars)
print('自由变量名列表:',code.co_freevars)
print('所使用常量列表:', code.co_consts)
print('引用名称列表:', code.co_names)

print('编译后的二进制操作码:', code.co_code)
print('地址及代码行号映射编码后的二进制:', code.co_lnotab)
print('操作标记值:', code.co_flags)
print('使用栈大小:', code.co_stacksize)

‘’’
函数代码所在文件路径: /Users/code/PycharmProjects/pythonProject4/demo2.py
函数代码第一行行号: 297
参数数量: 2
关键字限定参数数量: 0
位置限定参数数量: 0
局部变量数量: 2
局部变量名列表: ('a', 'b')
cell变量名列表: ()
自由变量名列表: ()
所使用常量列表: ('加法函数',)
引用名称列表: ()
编译后的二进制操作码: b'\x97\x00|\x00|\x01z\x00\x00\x00S\x00'
地址及代码行号映射编码后的二进制: b'\x02\x02'
操作标记值: 3
使用栈大小: 2
’‘’

位置限定参数及关键字限定参数

def sub(a, b, /):  # 限定位置参数
    return a -b

def mul(*, a, b):  # 限定关键字参数
    return a * b

print('sub函数-参数数量:', sub.__code__.co_argcount)
print('sub函数-位置限定参数数量:', sub.__code__.co_posonlyargcount)

print('mul函数-参数数量:',mul.__code__.co_argcount)
print('mul函数-关键字限定参数数量:',mul.__code__.co_kwonlyargcount)

‘’'
sub函数-参数数量: 2
sub函数-位置限定参数数量: 2
mul函数-参数数量: 0
mul函数-关键字限定参数数量: 2
‘''

引用函数名称列表 

def add(a, b):
    return a + b

def mul(a, b):
    return a * b


def calc(x, y, z):
    """计算(x+y) * z"""
    return mul(add(x, y), z)  # 引用名称

print('calc函数-引用函数名列表:', calc.__code__.co_names)

‘’'
calc函数-引用函数名列表: ('mul', 'add’)
‘''

闭包及外部函数

def calc(method, a, b):
    """延迟计算"""
    def add():
        """计算加法"""
        return a + b

    if method == 'add':
        return add
    # ...

add = calc('add', 1, 2)  # 闭包函数


print('calc函数-cell变量名列表:', calc.__code__.co_cellvars)
print('calc函数-局部变量名列表:', calc.__code__.co_varnames)
print('calc函数-所使用常量列表:', calc.__code__.co_consts)

print('add函数-自由变量名列表:', add.__code__.co_freevars)
print('add函数-局部变量名列表:', add.__code__.co_varnames)
print('add函数-所使用常量列表:', add.__code__.co_consts)

‘’'
calc函数-cell变量名列表: ('a', 'b')
calc函数-局部变量名列表: ('method', 'a', 'b', 'add')
calc函数-所使用常量列表: ('延迟计算', , 'calc..add', 'add', None)
add函数-自由变量名列表: ('a', 'b')
add函数-局部变量名列表: ()
add函数-所使用常量列表: ('计算加法’,)
‘''

可调用对象原理

type()

type() 函数如果你只有第一个参数则返回对象的类型,三个参数返回新的类型对象。

语法格式:

type(object)
type(name, bases, dict)

参数:

  • name -- 类的名称。
  • bases -- 基类的元组。
  • dict -- 字典,类内定义的命名空间变量。

一个参数返回对象类型, 三个参数,返回新的类型对象。

# 一个参数实例
>>> type(1)

>>> type('runoob')

>>> type([2])

>>> type({0:'zero'})

>>> x = 1          
>>> type( x ) == int    # 判断类型是否相等
True
 
# 三个参数
>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))  # 产生一个新的类型 X
>>> X

type() 与 isinstance()区别:

class A:
    pass
 
class B(A):
    pass
 
isinstance(A(), A)    # returns True
type(A()) == A        # returns True
isinstance(B(), A)    # returns True
type(B()) == A        # returns False

基本原理

我们知道对象是如何被创建的,主要有两种方式,一种是通过Python/C API,另一种是通过调用类型对象。对于内置类型的实例对象而言,这两种方式都是支持的,比如列表,我们即可以通过[]创建,也可以通过list(),前者是Python/C API,后者是调用类型对象。

但对于自定义类的实例对象而言,我们只能通过调用类型对象的方式来创建。而一个对象如果可以被调用,那么这个对象就是callable,否则就不是callable。

而决定一个对象是不是callable,就取决于其对应的类型对象中是否定义了某个方法。如果从 Python 的角度看的话,这个方法就是 __call__。

可以使用

callable(func)

hasattr(type, "__call__")

来检查这个对象是否为可调用对象

def func1():
    print('func1() run')

print(type(func1)) #
print(type(int)) #
print(hasattr(func1, "__call__")) #true
print(hasattr(int, "__call__")) #true
print(callable(func1))
print(callable(int))

不仅函数、基本类型有__call__方法,而且在自定义类中,我们增加__call__方法后,自定义的类对象也是可以被调用的: 

class Adder:
    def __init__(self, value=0):
        self.data = value
 
    def __call__(self, x):
        return self.data + x
 
add = Adder()
print(add(1)) # 1
print(add(2)) # 2

上面这个类 Adder 有一个__call__ 方法,该方法接收一个参数x,并返回data + x 的值。我们可以通过实例来进行调用,就像函数调用一样,如上面代码中的add(1) 和add(2),他会自动调用__call__方法,并返回结果。

使用__call__ 方法可以使一个类的实例具有“函数”的行为。

总之,使用 call 方法可以使类的实例具有可调用的特性,使得类的实例可以像函数一样被调用,这能带来很多便利和可能性,增加程序的灵活性和可扩展性。

你可能感兴趣的:(python,开发语言,前端)