5.4 5.4 5.4 可调用对象
除了用户定义的函数,调用运算符(即())还可以应用到其他对象上。如果想判断对象能否调用,可以使用内置的callable()函数。Python的数据模型文档列出了以下7种可调用对象。
-
用户定义的函数
使用def语句或lambda表达式创建 -
内置函数
使用C语言(CPython)实现的函数,如len或者time.strtime -
内置方法
使用C语言实现的方法,如dict.get -
方法
在类的定义体中定义的函数 -
类
调用类时会运行类的__new__方法创建一个实例,然后运行__init__方法,初始化该实例,然后把实例返回给调用方。因为 Python 没有new运算符,所以调用类时相当于调用函数。(通常,调用类会创建那个类的实例,不过覆盖__new__方法的话,也可能出现其他行为。) -
类的实例
如果类定义了__call__方法,那么它的实例可以作为函数调用。 -
生成器函数
使用yield关键字的函数或方法。调用生成器函数返回的是生成器对象。
python中的可调用类型有很多,因此可以通过内置函数callable()来判断对象是否可调用。
用户自定义函数 ———— 每个函数名都有一个值,该值的类型是可以被解释器识别为用户定义函数的类型
(还是用了上一节中的例子?)
>>> [callable(i) for i in (fruits, func1, 12, 'hello')]
[False, True, False, False]
>>> [callable(i) for i in (my_func, int, str)]
[True, True]
5.5 5.5 5.5 用户定义的可调用类型
import random
class BingoCase:
def __init__(self, items):
self._items = list(items)
random.shuffle(self._items)
def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCase')
def __call__(self, *args, **kwargs):
return self.pick()
bc = BingoCase(range(4))
print(bc.pick()) # 3
print(bc()) # 0
print(callable(bc)) # True
实现__call__方法的类是创建函数类对象的简便方法。
我们把这个类型的对象当作函数来使用,相当于重载了括号运算符。
5.6 5.6 5.6 函数内省
函数对象有很多很多的属性,除了我们之前提到的__doc__还有以下常用属性:
属性 | 说明 |
---|---|
doc | 用于获取函数的文档说明,如果没有,则返回 None |
name | 获取函数的名称 |
module | 返回函数所在的模块,如果无则返回None |
defaults | 以元组的形式返回函数的默认参数,如果无默认参数则返回None |
dict | 包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象 |
class | 该实例的类对象 |
self | 仅方法可用,如果是绑定的(bound),则指向调用该方法的类(如果是类方法)或实例(如果是实例方法),否则为None |
示例如下:
>>> func1.__doc__
' return n!'
>>> func1.__name__
'func1'
>>> func1.__class__
# <class 'function'>
'__iter__'
>>> func1.__qualname__
'func1'
>>> func1.__module__
'__main__'
>>> func1.__code__
<code object func1 at 0x103c5c270, file "<stdin>", line 1>
>>> func1.__globals__
"""
{'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>, 'func1': <function func1 at 0x103c67b70>, 'my_func': <function func1 at 0x103c67b70>, 'fruits': ['strawberry', 'apple', 'watermalon', 'lemon'],
'reverse': <function reverse at 0x10360ce18>, 'reduce': <built-in function reduce>,
'add': <built-in function add>}
"""
从定位参数到仅限关键字参数
python中有一个很好的特性就是提供了极为灵活的参数处理机制。*args, **kwargs这两个不定参数可以帮我们省去很多麻烦。
在调用函数时,使用*或者**可以展开迭代对象,从而映射到单个参数。
示例如下:
>>> def books(name, cls=None, *content, **attrs):
... if cls is not None:
... attrs['class'] = cls
... if attrs:
... attr_str = ''.join(' %s="%s"' % (attr, value)
... for attr, value in sorted(attrs.items()))
... else:
... attr_str = ''
... if content:
... return '\n'.join('<%s%s>%s<%s>' % (name, attr_str, c, name)
... for c in content)
... else:
... return '<%s%s />' % (name, attr_str)
>>> books('br')
'<br />'
>>> books('p', 'hello')
'<p class="hello" />'
>>> books('p', div, 'hello', 'world')
'<p class="div">hello<p>\n<p class="div">world<p>'
>>> my_tag = {'name': 'img','title':'maixue','cls':'framed','src': 'sun.jpg'}
>>> books(**my_tag)
'<img class="framed" src="sun.jpg" title="maixue" />'
在my_tag前面加上了**之后,字典中的所有元素都作为单个参数传入,同名的键就会自动绑定在指定位置,余下的则被**attrs捕获。