python有以下几种可调用对象:
本文主要学习函数对象。
在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__']
‘''
函数可以做什么?
可以将函数作为变量一样使用
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(object) type(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 方法可以使类的实例具有可调用的特性,使得类的实例可以像函数一样被调用,这能带来很多便利和可能性,增加程序的灵活性和可扩展性。