PEP8——Python编码风格

PEP8: Python代码风格

从Python官方文档中翻译、整理的一个笔记,是Python代码风格方面的约定。

蛮目的追求一致性是误入魔道

Guido认为在程序开发中,大部分时间是花在看代码上,因此可读性非常重要。

制定风格指南是为了一致性,一致的编码风格是重要的,一致的项目风格是重要的,一致的模块或函数风格就更加重要。

但更重要的是知道什么时候不保持一致:当风格指南变得不适用的时候。当犹豫不决时,听从你的判断力,看看其它代码然后决定怎么样做看起来更好。不要羞于提问。

原则上来讲,不要为了使用PEP8风格指南而忽略向后兼容性。

但有时候出于下列原因可以不遵循这个指南:

  • 哪怕根据PEP8来阅读代码,适用指南还是会减低代码可读性
  • 为了和其它没有遵循此指南的代码保持一致(可能是历史原因),但这也是清除烂代码的时候(根据敏捷开发风格)
  • 很久远的代码,旧到那会儿还没有PEP8,也没有其它的原因去改动代码
  • 但代码需要和旧版本的Python保持兼容,低版本的Python可能不支持PEP8风格中的某些特性

代码排版

缩进

使用4个空格缩来表示缩进。

连续的代码行应该保持一样的缩进,不管是使用Python语句中的中括号、圆括号,还是悬挂缩进。当使用悬挂缩进的时候,在第一行不要包含参数,而缩进要能够明确的区分每行内容。

正确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 和开始的中括号对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 增加缩进以和其它代码区分开来
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# 悬挂缩进要增加一个缩进级别
foo = long_function_name(
var_one, var_two,
var_three, var_four)

错误:

1
2
3
4
5
6
7
8
9
# 没有对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 缩进不够
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)

4个空格表示一级缩进的规定是可以灵活使用的:

1
2
3
4
# 这种缩进可能不一定需要使用4个空格
foo = long_function_name(
var_one, var_two,
var_three, var_four)

当if语句长到需要跨行的时候,没有必要改成两个if语句,增加一个空格,增加一个开括号,然后下一行保持缩进即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 没有额外的缩进
if (this_is_one_thing and
that_is_another_thing):
do_something()
# 增加一条注释,在某些编辑器会语法高亮,从而起到区分作用
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# 增加一些缩进
if (this_is_one_thing
and that_is_another_thing):
do_something()

代码块结束符如中括号、圆括号等在多行代码的情况下,可以和上一行第一个非空白字符保持缩进:

1
2
3
4
5
6
7
8
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)

也可以直接和上一行第一个字符保持缩进:

1
2
3
4
5
6
7
8
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)

制表符vs空格

推荐使用空格表示缩进。

只有当之前使用了制表符,为了保持统一的缩进而不得不使用制表符的时候才使用制表符,能不用就不要用。

在Python 3不允许混用空格和制表符。

在Python 2中混用了空格和制表符的应该换成只使用空格来表示缩进。

在Python 2与命令行中执行时,加上-t参数,会警告混用了制表符与空格的情况,如果使用-tt参数则变成错误看待。强烈推荐使用!

行最大长度

行最大长度不要超过79个字符。

对于大段的包含一定结构的字符串(文档字符串或注释),行的长度不要超过72个字符。

限制长度可以让编辑器同时打开多个文件,方便一一对照来查看、比较多个版本的代码。

如果要使用很长的行,则推荐使用 =\= 来连接:

1
2
3
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())

也可以在操作符后面断行,但要保持缩进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle(Blob):
def __init__(self, width, height,
color='black', emphasis=None, highlight=0):
if (width == 0 and height == 0 and
color == 'red' and emphasis == 'strong' or
highlight > 100):
raise ValueError("sorry, you lose")
if width == 0 and height == 0 and (color == 'red' or
emphasis is None):
raise ValueError("I don't think so -- values are %s, %s" %
(width, height))
Blob.__init__(self, width, height,
color, emphasis, highlight)

空行

顶级函数和类定义前后应该留2个空行。

在类里面的函数定义之间保留1个空行。

可以多加1个空行来分隔一组相关函数。而对于一堆只有1行的内容之间,有时可以省去空行分隔。

在函数里可以适当的添加空行来组织逻辑。

Python支持特殊字符Crtl-L(^L),一些工具会把这些字符看做分页符,因此可以在文件中把这些字符当作分页符使用。但有些编辑器或基于web的阅读器并不会 =^L= 看成特殊字符,而是直接显示这个字符。

源文件编码格式

Python发行版核心代码一般都是UTF-8编码(Python 2使用ASCII).

源文件在Python 2中使用ASCII或在Python 3中使用UTF-8应该不会遇到编码问题。

在标准库中,不是使用默认编码格式的应该只用于测试或者注释/文档的作者名字包含非ASCII字符。总之,在字符串中包含非ASCII码最好使用在前面添加 =\x=, =\u=, =\U=, =\N= 的方式.

从Python 3.0之后,标准库要符合下列要求(参考 PEP 3131):

  • 所有标识符必须只包含ASCII字符,并且应该尽量使用英文单词(有时,有些缩写或技术术语并非使用英文)
  • 尽量保持字符串、注释使用ASCII,例外情况包括:
    • 测试用例需要测试到和非ASCII有关的功能
    • 作者名字包含了非拉丁字符

开源项目的受众是全球的开发人员,应该也遵守这些条款。

导入(import)

导入时应该一行一行的分开,如:

正确做法:

1
2
import os
import sys

错误做法:

1
import sys, os

但这样做是可以的:

1
from subprocess import Popen, PIPE

导入部分代码应该总是在源文件的前几行,紧跟在模块注释或文档之后,在模块全局变量、声明之前。

导入部分应该按照下列顺序分组导入:

  • 导入标准库
  • 导入相关第三方库
  • 导入本地程序/库

在每组之间应该用一个空行分割.

在导入部分代码后面,紧跟着指定 =all= 。

建议使用绝对路径导入,这样有更好的可读性,同时在导入操作出问题时更好定位问题根源:

1
2
3
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

当然,使用相对路径导入也是可以接受的,尤其是当软件包文件结构很复杂的时候,使用绝对路径导入显得没有必要:

1
2
from . import sibling
from .sibling import example

标准库的代码应该避免过于复杂的包文件结构,总是使用绝对路径导入。

在Python 3中已经不支持相对路径导入了,因此,不要使用相对路径导入.

但从一个包含类的模块中导入一个类,一般可以这样:

1
2
from myclass import MyClass
from foo.bar.yourclass import YourClass

如果这样会导致本地变量名混淆,则可以这样:

1
2
import myclass
import foo.bar.yourclass

然后使用 =myclass.MyClass= 和 =foo.bar.yourclass.YourClass= 来调用。

尽量避免一股脑的全导入(=from import *=),这样无法知晓到底哪些导入到了名字空间,减低了可读性,也导致一些自动化工具无法工作。有一种情况下可以使用全部导入,就是要把内部结构当作外部公共API的时候。

即使是一股脑全部导入的方式,此文档后续部分依旧适用于公共、内部接口。

字符串引号

在Python语言中,单引号和双引号是一样的,但一个字符串中已经有单引号或双引号时,可以使用另一个来避免转义字符,这样可以增加可读性。

对于一大段字符串,建议使用双引号来作为 =文档字符串= ,参阅 PEP 257.

表达式和语句中的空格

口头禅

在下列情况下避免无关的空格:

  • 紧挨着中括号、圆括号、大括号时
1
2
Yes: spam(ham[1], {eggs: 2})
No: spam( ham[ 1 ], { eggs: 2 } )
  • 紧挨着逗号、冒号、分号时
1
2
Yes: if x == 4: print x, y; x, y = y, x
No: if x == 4 : print x , y ; x , y = y , x
  • 在切片(slice)中,冒号起到操作符的作用,在符号两边的数量应该相等,即两边要有一样的空格数据量。

Yes:

1
2
3
4
5
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]

No:

1
2
3
4
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
  • 函数调用时括号前面不需要空格
1
2
Yes: spam(1)
No: spam (1)
  • 索引或切片中括号前不需要空格
1
2
Yes: dct['key'] = lst[index]
No: dct ['key'] = lst [index]
  • 不必为了对齐,而在等号左右添加空格

Yes:

1
2
3
x = 1
y = 2
long_variable = 3

No:

1
2
3
x = 1
y = 2
long_variable = 3

其它建议

  • 在这些操作符前后添加1个空格:=,+=,-=,==,<,>,!=,<>,<=,>=,in,not in,is,is not,and,or,not
  • 如果操作符有不同的优先级,则在低优先级的符号前后加空格,视情况而定,但绝不要多余1个空格,并且要加空格就要保证前后空格一样多

Yes:

1
2
3
4
5
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

No:

1
2
3
4
5
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
  • 当等号用于传递参数或定义默认参数时,前后不要空格

Yes:

1
2
def complex(real, imag=0.0):
return magic(r=real, i=imag)

No:

1
2
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
  • 不要将多条语句混合在一起

Yes:

1
2
3
4
5
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()

尽量不要:

1
2
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
  • 有时候,将if/for/while的语句块放在同一行是可以的;但不可以是多行,更好避免过长的代码行

尽量不要:

1
2
3
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()

绝对不要:

1
2
3
4
5
6
7
8
9
10
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()
try: something()
finally: cleanup()
do_one(); do_two(); do_three(long, argument,
list, like, this)
if foo == 'blah': one(); two(); three()

注释

如果注释会误导对代码的理解,还不如没有注释; 当代码逻辑改变时,一定要及时更新注释。

注释应该是完整的句子。如果注释是一个词组或句子,则首字母应该大写,除非它本身是一个小写字母开头的标识符。

如果注释很短,后面的句号可以省略。注释块由多个句子组成段落,则每句后面都要有句号。

每句句号后面要有空格。

当使用英文写注释时,请阅读 =William Strunk Jr.和E. B. White书籍《The Elements of Style》=

非英文国家的Python开发者: 请使用英文写注释,除非你百分之百确定都代码的人都懂自己本国语言。

注释块

注释块后面一般跟着代码,注释和代码的缩进是一致的。每个注释行以 =#= 开头;注释块里面分段是用单独的 =#= 作为一行分割

单行注释

尽量少用单行注释。

单号注释一般和要注释的代码语句保持在一行,和代码之间至少要有2个空格的空间。

单行注释一般是没必要的,如果语句已经很明确了,就更没有必要单行注释了。

不要这样写:

1
x = x + 1 # Increment x

但有时这样是有用的:

1
x = x + 1 # Compensate for border

文档字符串注释

详细说明如何写好文档字符串,参考PEP 257:

  • 为所有公共模块、函数、类、方法加上文档字符串
  • PEP257讲述了如何写好文档字符串,一般结束文档字符串的 """ 应该单独一行:
1
2
3
4
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
  • 对于只有一行的文档字符串,则可以将 """ 结束符也在同一行

版本控制

如果你是要SVN、CVS、RCS,则:

1
2
__version__ = "$Revision$"
# $Source$

这些行应该紧跟着模块文档字符串后面,前面不要有其它任何内容,前后添加空行来分隔。

命名规范

Python的命令规范有点乱,这些是目前的惯例,新的模块或软件包应该遵循这些命名规范,但已经存在的旧软件包则保持内部的一致性即可。

首要原则

面向用户的公共API命名应该反映如何使用,而不是如何实现。

描述:命名风格

有很多种命名风格,根据使用目的来决定如何命名,下列命名风格是优雅的:

  • b (单个小写字母)
  • B (单个大写字母)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(每个单词首字母大写,对于缩写全部使用大写字母)
  • mixedCase (首字母小写,其它单词大写字母开头)
  • Capitalized_Words_With_Underscores (丑死了!)

有一种风格是使用某个前缀来表示一组相关的名字,但在Python中使用的不多,除非是为了某种整体性。

软件包X11的所有公共函数都用X开头,但在Python里面这个没有必要,因为变量和方法名前面都已经有了调用的对象,函数名前面也会包含模块名。

有些时候,下面这种特殊命名是可以接受的:

  • =_single_leading_underscore= : 作为私有,当使用不会导入这种以下划线开头的变量或函数

    1
    from M import *
  • =single_trailingunderscore= : 为了防止和Python关键字冲突,如

    1
    Tkinter.Toplevel(master, class_='ClassName')
  • =double_leading_underscore= : 定义类变量时,重新生成调用名,如在类FooBar中, =boo= 变成 =_FooBar__boo=

  • =double_leading_and_trailing_underscore= : 可以存在用户控制命名空间的魔法对象,如 =init=, =import= 或 =file= 。永远不要使用这样的命名,知道它们存在就够了!

规定: 命名惯例

避免使用的命名

永远不要使用 =l=, =O=, =I= 单个字符作为变量名,在某些字体里面很容易和其它字母混淆。

包和模块名字

模块名应该全由小写字母组成的简短字符串,如果能够提高可读性,也可以使用大写字母。

软件包名应该全由小写字母组成的简短字符串,严禁使用下划线命名。

但使用C/C++开发的扩展模块时,可能提供的接口会有下划线,如 =_socket=

类名字

类名字每个单词的开头字母应该大写。

异常名字

因为异常一般是类,要遵循类命名规则,但后缀必须有Error.

全局变量名

假设这些变量只在模块内部使用,这个规范也适用于函数内部。

如果模块内部的变量要这样使用:

1
from M import *

那么应该使用 =all= 来提供导出的变量名,或者通过以下划线开头作为私有变量来防止被导入其它名字空间。

函数名

函数名应该是小写字母组成,如果有多个单词则用下划线连接起来,这样可以提高可读性。

混合大小写尽量避免,除非是为了以前的代码需要保持兼容性。

函数和方法参数名

对于实例方法的第一个参数总是 =self=

对于类方法的第一个参数总是 =cls=

如果函数参数和关键字冲突了,可以在后面添加一个下划线,而不建议另外取一个奇怪的名字,如 =class_= 就比 =clss= 好。

方法名与实例变量

使用函数命名规则:全部小写,如果需要就用下划线分割单词。

只有私有方法或变量才会以一个下划线开头。

为了避免和子类命名冲突,可以用两个下划线开头命名;根据Python特性,如果类Foo有一个变量 =a= ,则无法通过 =Foo.a= 来访问,需要通过 =Foo._Foo__a= 的形式来访问。但不要滥用,只有为了和子类避免冲突才会这样命名。

常数

常数一般是模块级别的,所有字母都大写,用下划线连接,如MAX_OVERFLOW,TOTAL

专为继承而设计

要常考虑一个类的方法和实例应该设置为公有还是非公有,如果不好决定,那么先设置为非公有,因为非公有变为公有相对好办,反过来则有更大的风险。

公有变量是给别人使用的,应该避免变动,以免造成使用这些公有变量的程序出问题。非公有变量是不会被第三方使用到,不管是改变还是删除都应该没有问题,只要保证不对其它第三方造成错误。

我们不使用『私有』这个说法,因为在Python里面没有真正意义上的『私有』。

还有一类属于『子类API』的变量,在其它语言一般叫做『保护』变量;有些类是设计来被继承的,不管是通过导入还是重载的方式。当设计这样一个类时,要认真考虑这些问题:

  • 对于哪个变量要设为『公有』
  • 哪个会作为子类API
  • 哪个是确实只会在类内部使用,

这些是一些Pythonic的做法:

  • 公有变量不应该以下划线开头
  • 如果公有变量和关键字冲突,在公有变量后面增加一个下划线。最好使用正确的拼写,但也不是那么绝对,比如 =cls= 就一般代表 =class=
  • 对于简单的数据变量,最好直接使用变量名,不必增加设置、访问方法。Python很容易提高性能,数据变量访问可以获得相对高的性能,使用属性访问的方式代替函数调用是可以提高性能的一种访问语法。
  • 如果类被子类继承,但有些变量不想让子类使用到,则可以命名的时候以双下划线开头,这样可以避免在子类里面造成变量名污染。

公共接口与内部接口命名

用户应该很容易辨别公共接口和内部接口。

一般带文档化接口是公共的,除非这些文档是为内部接口定义的;所有没有文档的接口都默认是内部接口。

为了更好的排查错误,模块的所有公共API应该使用 =all= 变量来声明,当 =all= 是一个空列表时,则相当于模块没有公共API。

即使有了 =all= 这个措施,内部接口(包括包、模块、类、函数、变量或其它)还是需要以下划线开头。

当一个接口被内部名字空间包含时,这个接口也是内部的。

导入的名字应该始终作为一个实现细节来看待,其它模块不应该依赖于直接访问这些导入的名字,除非它们明确的在模块API里面用文档说明了,比如 =os.path= 或包的 =ini= 属于公开功能的子模块。

编程建议

  • 代码不应该排斥Python的其它实现版本,如PyPy, Jython, IronPython, Cython, Psyco等。比如,不要依赖于CPython连接字符串的高效实现,像 =a+=b= 或 =a=a+b= 形式。这种用法在CPython中也不是通用的,并非在所有实现版本都能很好的支持。为了性能提升,使用 ‘’.join() 应该会更好些,这样可以保证在所有版本中线性执行。
  • 对于 =None= 的比较,应该只用 =is= 和 =is not= ,不要使用等号;同样,如果你要表达 =if x is None= ,请直接使用 =if x= 即可。
  • 使用 =is not= 操作符,而不是 =not…is=:
    Yes:

    1
    if foo is not None:

    No:

    1
    if not foo is None:
  • 当要实现比较操作时,最好同时实现6个操作: =eq= , =ne= , =lt= , =le=, =gt= , =ge=;另外, =functools.total_ordering()= 可以自动生成剩下的比较操作方法。

  • 使用 =def= 明确定义函数,而不要通过赋值语句使用 =lambda= 形式的匿名函数:
    Yes:

    1
    def f(x): return 2*x

    No:

    1
    f = lambda x: 2*x
  • 从 =Exception= 衍生出异常类,而不是从 =BaseException= 直接衍生,直接继承 =BaseException= 往往导致捕获异常时出错。设计异常继承时,知道异常具体信息比知道异常发生的位置更重要。

  • 尽量使用异常链,在Python 3中,使用raise X from Y来代替直接丢弃异常回溯路径。
  • 在Python 2中触发异常,使用 =raise ValueError(‘message’)=,而不是使用 =raise ValueError, ‘message’= ;后一种语法在Python 3中已经不支持了。
  • 当捕获异常时,尽量指明是哪种异常,而不是使用except: 其它子句:

    1
    2
    3
    4
    try:
    import platform_specific_module
    except ImportError:
    platform_specific_module = None

    使用 =except: 其它子句= 的形式会捕获 =SystemExit=, =KeyboardInterrupt= ,从而导致很难获取 =Ctrl-C= 并导致其它问题。如果想捕获所有的异常,可以使用 =except Exception:= ,而之前的错误用法等同于 =except BaseException:=

    可以这样更好的使用 =except=:

    • 如果有异常处理程序,则应该会打印或记录日志,或者至少要报告发现了错误
    • 如果程序要做一些清理工作,可以通过 =raise= 触发一个异常,然后在 =try…finally= 进行处理,这种方式比较好
  • 如果要将异常绑定一个名字,推荐使用Python 2.6增加的语法:

    1
    2
    3
    4
    try:
    process_data()
    except Exception as exc:
    raise DataProcessingFailedError(str(exc))

    在Python 3中只支持这种语法,从而避免以前使用逗号分隔语法造成的问题。

  • 当捕获系统错误,最好使用Python 3.3中的详细异常说明,而不是仅仅一个errno
  • 一般情况下,对于try/except语句,尽量减少try里面的代码量,这样可以避免引入Bug:
    Yes:

    1
    2
    3
    4
    5
    6
    try:
    value = collection[key]
    except KeyError:
    return key_not_found(key)
    else:
    return handle_value(value)

    No:

    1
    2
    3
    4
    5
    6
    try:
    # Too broad!
    return handle_value(collection[key])
    except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)
  • 当本地使用到某些资源时,使用 =with= 来保证进行了资源的释放等工作,也可以使用 =try/finall= 语句来处理。

  • 不论是获取资源,还是释放资源,上下文管理器都应该操作一个独立的函数或方法:

    Yes:

    1
    2
    with conn.begin_transaction():
    do_stuff_in_transaction(conn)

    No:

    1
    2
    with conn:
    do_stuff_in_transaction(conn)

    后一种用法没有为__enter____exit__提供任何信息,从而不知道何时要开始做什么或者何时要关闭连接。这里明确性很重要。

  • 要重视 =return= 语句。一个函数的返回语句要么返回一个表达式,要么什么也不返回。如果返回的是表达式,但没有值可以返回的时候,应该返回None;同时,比较明确的返回值应该放到函数的后面,增加可读性.

    Yes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def foo(x):
    if x >= 0:
    return math.sqrt(x)
    else:
    return None
    def bar(x):
    if x < 0:
    return None
    return math.sqrt(x)

    No:

    1
    2
    3
    4
    5
    6
    7
    8
    def foo(x):
    if x >= 0:
    return math.sqrt(x)
    def bar(x):
    if x < 0:
    return
    return math.sqrt(x)
  • 使用字符串的方法,而不是使用 =string= 模块。字符串方法一般更快,并且使用相同的API。

  • 尽量使用 ‘’.startswith(),’’.endswith()

    1
    2
    Yes: if foo.startswith('bar'):
    No: if foo[:3] == 'bar':
  • 比较对象的类型,应该直接使用 =isinstance()=

    1
    2
    Yes: if isinstance(obj, int):
    No: if type(obj) is type(1):

    当检测一个对象是否是字符串,要记住这也可能是 =unicode= 字符串。在Python 2中, =str= 和 =unicode= 有一个共同的基类 =basestring= ,因此可以这样:

    1
    if isinstance(obj, basestring):

    但要记住,在Python3中, =unicode= 和 =baseString= 都不存在了,并且 =bytes= 对象不再属于字符串,而是看做整数序列。

  • 对于序列,包括字符串、列表、元组,空序列等同于 =false= :

    1
    2
    3
    4
    5
    Yes: if not seq:
    if seq:
    No: if len(seq)
    if not len(seq)
  • 不要依赖于行尾的空白符,有些编辑器可能不显示后面的空白符,可以参看 =reindent.py=

  • 不用用 == 比较布尔值:
    1
    2
    3
    Yes: if greeting:
    No: if greeting == True:
    Worse: if greeting is True:
吴羽舒 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!