《流畅的 Python 》第 5 章笔记


示例 5-10 这段代码有点费解。

def tag(name, *content, cls=None, **attrs): // ①
    """生成一个或多个 HTML 标签"""
    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'%
                         (name, attr_str, c, name) for c in content)
    else:
        return '<%s%s />'%(name, attr_str)
print(tag('br'))
print(tag('p', 'hello'))
print(tag('p', 'hello', 'world'))
print(tag('p', 'hello', id=33))
print(tag('p', 'hello', 'world', cls='sidebar'))
print(tag(content='testing', name='img'))
my_tag = {'name':'img', 'title':'Sunset Boulevard', 'src':'sunset.jpg', 'cls':'framed'}
print(tag(**my_tag))

输出:
content ()

content ('hello',)

hello

content ('hello', 'world')

hello

world

content ('hello',)

hello

content ('hello', 'world') content () content ()

① 这里的 * 和 ** 代表包裹传递。具体可以参见这篇:

② 参数 attrs 类型是字典,默认为 {},所以 bool(attrs) 为 False,反之则为 True。

③ 参数 content 类型是元组,默认为 (),所以 bool(content) 为 False,反之则为 True。

所以这个函数在调用时真正必需的参数只有 name。

我们知道,在参数的几种传递方式混合使用的情况下,在定义或者调用参数时,要注意一个顺序的基本原则:先位置,再关键字,再包裹位置,再包裹关键字。

回到书中这个例子,由于这个函数是 “位置传递”、“包裹位置传递”、“关键字传递”、“包裹关键字传递” 的顺序,所以,在了解书中的几种乱序情况下,我们还需要记住,我们按照参数定义的顺序进行调用时,要小心行事。