diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md new file mode 100644 index 0000000..59885fc --- /dev/null +++ b/source/c01/c01_32.md @@ -0,0 +1,31 @@ +# 1.32 如何让大数变得更易于阅读? + +![](http://image.iswbm.com/20200804124133.png) + +当一个数非常大时,可能过百万,也可能上亿,太多位的数字 ,会给我们阅读带来很大的障碍。 + +比如下面这个数,你能一下子说出它是多少万呢,还是多少亿呢? + +``` +281028344 +``` + +是不是没法很快的辩识出来? + +这时候,你可以使用 `_` 来辅助标识,写成这样子就清晰多了 + +``` +281_028_344 +``` + +关键这种写法,在代码中并不会报错噢(Python2 不支持) + +```python +>>> number=281_028_344 +>>> number +281028344 +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst new file mode 100644 index 0000000..96c6234 --- /dev/null +++ b/source/c01/c01_32.rst @@ -0,0 +1,35 @@ +1.32 如何让大数变得更易于阅读? +=============================== + +|image0| + +当一个数非常大时,可能过百万,也可能上亿,太多位的数字 +,会给我们阅读带来很大的障碍。 + +比如下面这个数,你能一下子说出它是多少万呢,还是多少亿呢? + +:: + + 281028344 + +是不是没法很快的辩识出来? + +这时候,你可以使用 ``_`` 来辅助标识,写成这样子就清晰多了 + +:: + + 281_028_344 + +关键这种写法,在代码中并不会报错噢(Python2 不支持) + +.. code:: python + + >>> number=281_028_344 + >>> number + 281028344 + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_07.md b/source/c06/c06_07.md new file mode 100644 index 0000000..5e075ea --- /dev/null +++ b/source/c06/c06_07.md @@ -0,0 +1,35 @@ +# 6.7 利用 any 代替 for 循环 + +![](http://image.iswbm.com/20200804124133.png) + +在某些场景下,我们需要判断是否满足某一组集合中任意一个条件 + +这时候,很多同学自然会想到使用 for 循环。 + +```python +found = False +for thing in things: + if thing == other_thing: + found = True + break +``` + +但其实更好的写法,是使用 `any()` 函数,能够使这段代码变得更加清晰、简洁 + +```python +found = any(thing == other_thing for thing in things) +``` + +使用 any 并不会减少 for 循环的次数,只要有一个条件为 True,any 就能得到结果。 + +同理,当你需要判断是否满足某一组集合中所有条件,也可以使用 `all()` 函数。 + +```python +found = all(thing == other_thing for thing in things) +``` + +只要有一个不满足条件,all 函数的结果就会立刻返回 False + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_07.rst b/source/c06/c06_07.rst new file mode 100644 index 0000000..d5be221 --- /dev/null +++ b/source/c06/c06_07.rst @@ -0,0 +1,41 @@ +6.7 利用 any 代替 for 循环 +========================== + +|image0| + +在某些场景下,我们需要判断是否满足某一组集合中任意一个条件 + +这时候,很多同学自然会想到使用 for 循环。 + +.. code:: python + + found = False + for thing in things: + if thing == other_thing: + found = True + break + +但其实更好的写法,是使用 ``any()`` +函数,能够使这段代码变得更加清晰、简洁 + +.. code:: python + + found = any(thing == other_thing for thing in things) + +使用 any 并不会减少 for 循环的次数,只要有一个条件为 True,any +就能得到结果。 + +同理,当你需要判断是否满足某一组集合中所有条件,也可以使用 ``all()`` +函数。 + +.. code:: python + + found = all(thing == other_thing for thing in things) + +只要有一个不满足条件,all 函数的结果就会立刻返回 False + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_08.md b/source/c06/c06_08.md new file mode 100644 index 0000000..4a705d3 --- /dev/null +++ b/source/c06/c06_08.md @@ -0,0 +1,42 @@ +# 6.8 不同条件分支里应减少重合度 + +![](http://image.iswbm.com/20200804124133.png) + +如下是一个简单的条件语句模型 + +```python +if A: + +elif B: + +else: + +``` + +如果 code_block_2 和 code_block_1 的代码完全一致,那么应该想办法减少代码冗余。 + +这边举个例子,下面这段代码中 + +```python +def process_payment(payment): + if payment.currency == 'USD': + process_standard_payment(payment) + elif payment.currency == 'EUR': + process_standard_payment(payment) + else: + process_international_payment(payment) +``` + +其实更好的做法是用 in 来合并条件一和条件二 + +```python +def process_payment(payment): + if payment.currency in ('USD', 'EUR'): + process_standard_payment(payment) + else: + process_international_payment(payment) +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_08.rst b/source/c06/c06_08.rst new file mode 100644 index 0000000..ac69ff1 --- /dev/null +++ b/source/c06/c06_08.rst @@ -0,0 +1,46 @@ +6.8 不同条件分支里应减少重合度 +============================== + +|image0| + +如下是一个简单的条件语句模型 + +.. code:: python + + if A: + + elif B: + + else: + + +如果 code_block_2 和 code_block_1 +的代码完全一致,那么应该想办法减少代码冗余。 + +这边举个例子,下面这段代码中 + +.. code:: python + + def process_payment(payment): + if payment.currency == 'USD': + process_standard_payment(payment) + elif payment.currency == 'EUR': + process_standard_payment(payment) + else: + process_international_payment(payment) + +其实更好的做法是用 in 来合并条件一和条件二 + +.. code:: python + + def process_payment(payment): + if payment.currency in ('USD', 'EUR'): + process_standard_payment(payment) + else: + process_international_payment(payment) + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_09.md b/source/c06/c06_09.md new file mode 100644 index 0000000..4bfd601 --- /dev/null +++ b/source/c06/c06_09.md @@ -0,0 +1,68 @@ +# 6.9 如无必要,勿增实体噢 + +![](http://image.iswbm.com/20200804124133.png) + +## 删除没必要的调用`keys()` + +字典是由一个个的键值对组成的,如果你遍历字典时只需要访问键,用不到值,有很多同学会用下面这种方式: + +```python +for currency in currencies.keys(): + process(currency) +``` + +在这种情况下,不需要调用keys(),因为遍历字典时的默认行为是遍历键。 + +```python +for currency in currencies: + process(currency) +``` + +现在,该代码更加简洁,易于阅读,并且避免调用函数会带来性能改进。 + +## 简化序列比较 + +我们经常要做的是在尝试对列表或序列进行操作之前检查列表或序列是否包含元素。 + +```python +if len(list_of_hats) > 0: + hat_to_wear = choose_hat(list_of_hats) +``` + +使用Python的方法则更加简单:如果Python列表和序列具有元素,则返回为True,否则为False: + +```python +if list_of_hats: + hat_to_wear = choose_hat(list_of_hats) +``` + +## 仅使用一次的内联变量 + +我们在很多代码中经常看到,有些同学分配结果给变量,然后马上返回它,例如, + +```python +def state_attributes(self): + """Return the state attributes.""" + state_attr = { + ATTR_CODE_FORMAT: self.code_format, + ATTR_CHANGED_BY: self.changed_by, + } + return state_attr +``` + +如果直接返回,则更加直观、简洁, + +```python +def state_attributes(self): + """Return the state attributes.""" + return { + ATTR_CODE_FORMAT: self.code_format, + ATTR_CHANGED_BY: self.changed_by, + } +``` + +这样可以缩短代码并删除不必要的变量,从而减轻了读取函数的负担。 + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_09.rst b/source/c06/c06_09.rst new file mode 100644 index 0000000..08455b4 --- /dev/null +++ b/source/c06/c06_09.rst @@ -0,0 +1,74 @@ +6.9 如无必要,勿增实体噢 +======================== + +|image0| + +删除没必要的调用\ ``keys()`` +---------------------------- + +字典是由一个个的键值对组成的,如果你遍历字典时只需要访问键,用不到值,有很多同学会用下面这种方式: + +.. code:: python + + for currency in currencies.keys(): + process(currency) + +在这种情况下,不需要调用keys(),因为遍历字典时的默认行为是遍历键。 + +.. code:: python + + for currency in currencies: + process(currency) + +现在,该代码更加简洁,易于阅读,并且避免调用函数会带来性能改进。 + +简化序列比较 +------------ + +我们经常要做的是在尝试对列表或序列进行操作之前检查列表或序列是否包含元素。 + +.. code:: python + + if len(list_of_hats) > 0: + hat_to_wear = choose_hat(list_of_hats) + +使用Python的方法则更加简单:如果Python列表和序列具有元素,则返回为True,否则为False: + +.. code:: python + + if list_of_hats: + hat_to_wear = choose_hat(list_of_hats) + +仅使用一次的内联变量 +-------------------- + +我们在很多代码中经常看到,有些同学分配结果给变量,然后马上返回它,例如, + +.. code:: python + + def state_attributes(self): + """Return the state attributes.""" + state_attr = { + ATTR_CODE_FORMAT: self.code_format, + ATTR_CHANGED_BY: self.changed_by, + } + return state_attr + +如果直接返回,则更加直观、简洁, + +.. code:: python + + def state_attributes(self): + """Return the state attributes.""" + return { + ATTR_CODE_FORMAT: self.code_format, + ATTR_CHANGED_BY: self.changed_by, + } + +这样可以缩短代码并删除不必要的变量,从而减轻了读取函数的负担。 + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_10.md b/source/c06/c06_10.md new file mode 100644 index 0000000..06d4360 --- /dev/null +++ b/source/c06/c06_10.md @@ -0,0 +1,25 @@ +# 6.10 保持代码的简洁与可诗性 + +![](http://image.iswbm.com/20200804124133.png) + +## 将条件简化为return语句 + +如果,我们实现的函数要返回一个布尔型的结果,通常会这样去做, + +```python +def function(): + if isinstance(a, b) or issubclass(b, a): + returnTrue + returnFalse +``` + +但是,对比这样,直接返回结果会更加明智: + +```python +def function(): + return isinstance(a, b) or issubclass(b, a) +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_10.rst b/source/c06/c06_10.rst new file mode 100644 index 0000000..e8be3b4 --- /dev/null +++ b/source/c06/c06_10.rst @@ -0,0 +1,29 @@ +6.10 保持代码的简洁与可诗性 +=========================== + +|image0| + +将条件简化为return语句 +---------------------- + +如果,我们实现的函数要返回一个布尔型的结果,通常会这样去做, + +.. code:: python + + def function(): + if isinstance(a, b) or issubclass(b, a): + returnTrue + returnFalse + +但是,对比这样,直接返回结果会更加明智: + +.. code:: python + + def function(): + return isinstance(a, b) or issubclass(b, a) + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_11.md b/source/c06/c06_11.md new file mode 100644 index 0000000..d737c2b --- /dev/null +++ b/source/c06/c06_11.md @@ -0,0 +1,57 @@ +# 6.11 给模块的私有属性上保险 + +![](http://image.iswbm.com/20200804124133.png) + +## 保护对象 + +有的朋友,喜欢简单粗暴的使用 `from x import *` 来导入 x 模块中的所有对象,实际上有一些对象或者变量,是实现细节,不需要暴露给导入方的,因为导入了也用不上。 + +对于这些变量或者对象,就可以在前面其名字前加上下划线,只要在变量名前加上下划线,就属于 "保护对象"。 + +使用 `from x import *` 后,这些 "保护对象" 是会直接跳过导入。 + +比如下面这些代码中,只有 drive 函数才会被 `from x import *` 所导入 + +```python +_moto_type = 'L15b2' +_wheel_type = 'michelin' + +def drive(): + _start_engine() + _drive_wheel() + +def _start_engine(): + print('start engine %s'%_moto_type) + +def _drive_wheel(): + print('drive wheel %s'%_wheel_type) +``` + +## 突破保护 + +前面之所以说是“保护”并不是“私有”,是因为Python没有提供解释器机制来控制访问权限。我们依然可以访问这些属性: + +```python +import tools +tools._moto_type = 'EA211' +tools.drive() +``` + +以上代码,以越过“保护属性”。此外,还有两种方法能突破这个限制,一种是将“私有属性”添加到tool.py文件的`__all__`列表里,使`from tools import *`也导入这些本该隐藏的属性。 + +```python +__all__ = ['drive','_moto_type','_wheel_type'] +``` + +另一种是导入时指定“受保护属性”名。 + +```python +from tools import drive,_start_engine +_start_engine() +``` + +甚至是,使用`import tools`也可以轻易突破保护限制。所以可见,“保护属性”是一种简单的隐藏机制,只有在`from tools import *`时,由解释器提供简单的保护,但是可以轻易突破。这种保护更多地依赖程序员的共识:不访问、修改“保护属性”。除此之外,有没有更安全的保护机制呢?有,就是下一部分讨论的私有变量。 + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_11.rst b/source/c06/c06_11.rst new file mode 100644 index 0000000..b93ac41 --- /dev/null +++ b/source/c06/c06_11.rst @@ -0,0 +1,64 @@ +6.11 给模块的私有属性上保险 +=========================== + +|image0| + +保护对象 +-------- + +有的朋友,喜欢简单粗暴的使用 ``from x import *`` 来导入 x +模块中的所有对象,实际上有一些对象或者变量,是实现细节,不需要暴露给导入方的,因为导入了也用不上。 + +对于这些变量或者对象,就可以在前面其名字前加上下划线,只要在变量名前加上下划线,就属于 +“保护对象”。 + +使用 ``from x import *`` 后,这些 “保护对象” 是会直接跳过导入。 + +比如下面这些代码中,只有 drive 函数才会被 ``from x import *`` 所导入 + +.. code:: python + + _moto_type = 'L15b2' + _wheel_type = 'michelin' + + def drive(): + _start_engine() + _drive_wheel() + + def _start_engine(): + print('start engine %s'%_moto_type) + + def _drive_wheel(): + print('drive wheel %s'%_wheel_type) + +突破保护 +-------- + +前面之所以说是“保护”并不是“私有”,是因为Python没有提供解释器机制来控制访问权限。我们依然可以访问这些属性: + +.. code:: python + + import tools + tools._moto_type = 'EA211' + tools.drive() + +以上代码,以越过“保护属性”。此外,还有两种方法能突破这个限制,一种是将“私有属性”添加到tool.py文件的\ ``__all__``\ 列表里,使\ ``from tools import *``\ 也导入这些本该隐藏的属性。 + +.. code:: python + + __all__ = ['drive','_moto_type','_wheel_type'] + +另一种是导入时指定“受保护属性”名。 + +.. code:: python + + from tools import drive,_start_engine + _start_engine() + +甚至是,使用\ ``import tools``\ 也可以轻易突破保护限制。所以可见,“保护属性”是一种简单的隐藏机制,只有在\ ``from tools import *``\ 时,由解释器提供简单的保护,但是可以轻易突破。这种保护更多地依赖程序员的共识:不访问、修改“保护属性”。除此之外,有没有更安全的保护机制呢?有,就是下一部分讨论的私有变量。 + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c06/c06_12.md b/source/c06/c06_12.md new file mode 100644 index 0000000..98fe966 --- /dev/null +++ b/source/c06/c06_12.md @@ -0,0 +1,31 @@ +# 6.12 变量不能与保留关键字重名 + +![](http://image.iswbm.com/20200804124133.png) + +在 Python 中有很多的保留关键字,这些关键字的使用,不需要我们定义,也不需要我们导入,只要你进入到了 Python 的环境中,就可以立即使用。 + +使用如下方法,可以查看 Python 中的保留关键字 + +```python +>>> import keyword +>>> keyword.kwlist +['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] +``` + +而很尴尬的是,如果你在日常编码中,不经意地用到其中的一些关键字,就会产生冲突。 + +比如说 class,这个有类别的意思,可能你也想使用它来作为变量名,如果直接使用,会发生冲突 + +更好的做法是,使用下划线来避免冲突 + +```python +def type_obj_class(name,class_): + pass + +def tag(name,*content,class_): + pass +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c06/c06_12.rst b/source/c06/c06_12.rst new file mode 100644 index 0000000..3c81868 --- /dev/null +++ b/source/c06/c06_12.rst @@ -0,0 +1,37 @@ +6.12 变量不能与保留关键字重名 +============================= + +|image0| + +在 Python +中有很多的保留关键字,这些关键字的使用,不需要我们定义,也不需要我们导入,只要你进入到了 +Python 的环境中,就可以立即使用。 + +使用如下方法,可以查看 Python 中的保留关键字 + +.. code:: python + + >>> import keyword + >>> keyword.kwlist + ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] + +而很尴尬的是,如果你在日常编码中,不经意地用到其中的一些关键字,就会产生冲突。 + +比如说 +class,这个有类别的意思,可能你也想使用它来作为变量名,如果直接使用,会发生冲突 + +更好的做法是,使用下划线来避免冲突 + +.. code:: python + + def type_obj_class(name,class_): + pass + + def tag(name,*content,class_): + pass + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c07/c07_11.md b/source/c07/c07_11.md new file mode 100644 index 0000000..42ba718 --- /dev/null +++ b/source/c07/c07_11.md @@ -0,0 +1,71 @@ +# 7.11 国际化模块,让翻译更优雅 + +![](http://image.iswbm.com/20200804124133.png) + +## 国际化与本地化 + +国际化 (internationalization),简称 **i18n** + +很多人并不知道,为什么要叫 i18n 呢?怎么谐音都不对。 + +实际上 18 是指在 ”internationalization” 这个单词中,i 和 n之间有18个字母。 + + + +而与之相对的,本地化(localization),简称 L10 n,10 就是指在 ”localization”这个单词中,l 和 n 之间有10个字母 + +本地化是指使一个国际化的软件为了在某个特定地区使用而进行实际翻译的过程。 + +国际化的软件具备这样一种能力,当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。 + +## gettext 模块 + +gettext 是一套 GNU下的国际化工具。主要有工具: + +- xgettext: 从源码中抽取字符串,生成po文件(portable object) +- msgfmt: 将po文件编译成mo文件(machine object) +- gettext: 进行翻译,如果找不到gettext命令,或者找不到msgfmt命令。请重新安装一遍gettext套件。 + + +很多系统中都内置了 gettext 模块 + +如果你在 ubuntu系统中,可能需要如下命令进行安装 + +``` +sudo apt-get install gettext +``` + +## 简单示例演示 + +首先新建一个目录 + +```shell +$ mkdir -p locale/zh_CN/LC_MESSAGES +``` + +然后在这个目录下新建一个 `hello.po` 文件 + +``` +msgid "hello world" +msgstr "你好世界" +``` + +然后执行如下一条命令,将 po 文件翻译成 mo文件 + +```shell +$ msgfmt locale/zh_CN/LC_MESSAGES/hello.po -o locale/zh_CN/LC_MESSAGES/hello.mo +``` + +然后在 local 同级目录下进入 Console 模式,就可以使用 `_` 进行翻译了,为什么 `_` 能这么用,原因是 `zh.install() ` 这个调用将其绑定到了 Python 内建命名空间中,以便在应用程序的所有模块中轻松访问它。 + +```python +>>> import gettext +>>> zh = gettext.translation("hello", "locale", languages=["zh_CN"]) +>>> zh.install() +>>> _('hello world') +'你好世界' +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst new file mode 100644 index 0000000..ef595c2 --- /dev/null +++ b/source/c07/c07_11.rst @@ -0,0 +1,80 @@ +7.11 国际化模块,让翻译更优雅 +============================= + +|image0| + +国际化与本地化 +-------------- + +国际化 (internationalization),简称 **i18n** + +很多人并不知道,为什么要叫 i18n 呢?怎么谐音都不对。 + +实际上 18 是指在 ”internationalization” 这个单词中,i 和 +n之间有18个字母。 + +而与之相对的,本地化(localization),简称 L10 n,10 就是指在 +”localization”这个单词中,l 和 n 之间有10个字母 + +本地化是指使一个国际化的软件为了在某个特定地区使用而进行实际翻译的过程。 + +国际化的软件具备这样一种能力,当软件被移植到不同的语言及地区时,软件本身不用做内部工程上的改变或修正。 + +gettext 模块 +------------ + +gettext 是一套 GNU下的国际化工具。主要有工具: + +- xgettext: 从源码中抽取字符串,生成po文件(portable object) +- msgfmt: 将po文件编译成mo文件(machine object) +- gettext: + 进行翻译,如果找不到gettext命令,或者找不到msgfmt命令。请重新安装一遍gettext套件。 + +很多系统中都内置了 gettext 模块 + +如果你在 ubuntu系统中,可能需要如下命令进行安装 + +:: + + sudo apt-get install gettext + +简单示例演示 +------------ + +首先新建一个目录 + +.. code:: shell + + $ mkdir -p locale/zh_CN/LC_MESSAGES + +然后在这个目录下新建一个 ``hello.po`` 文件 + +:: + + msgid "hello world" + msgstr "你好世界" + +然后执行如下一条命令,将 po 文件翻译成 mo文件 + +.. code:: shell + + $ msgfmt locale/zh_CN/LC_MESSAGES/hello.po -o locale/zh_CN/LC_MESSAGES/hello.mo + +然后在 local 同级目录下进入 Console 模式,就可以使用 ``_`` +进行翻译了,为什么 ``_`` 能这么用,原因是 ``zh.install()`` +这个调用将其绑定到了 Python +内建命名空间中,以便在应用程序的所有模块中轻松访问它。 + +.. code:: python + + >>> import gettext + >>> zh = gettext.translation("hello", "locale", languages=["zh_CN"]) + >>> zh.install() + >>> _('hello world') + '你好世界' + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md new file mode 100644 index 0000000..6962ed2 --- /dev/null +++ b/source/c07/c07_12.md @@ -0,0 +1,94 @@ +# 7.12 非常好用的调度模块 + +![](http://image.iswbm.com/20200804124133.png) + +Python 自带一个调度器模块`sched`,它能为你实现优先级队列/延迟队列和定时队列。 + +这个模块的使用非常简单,首先以延迟队列为例: + +```python +import sched + +def do_work(name): + print(f'你好:{name}') + +sch = sched.scheduler() +sch.enter(5, 1, do_work, argument=('iswbm', )) +sch.run() +``` + +代码运行以后,会卡在`sch.run()`这里,5秒钟以后执行`do_work('iswbm')`,运行效果如下图所示: + +![](http://image.iswbm.com/20210521215217.png) + +其中,`sch.enter()`的第一个参数为延迟的时间,单位为秒,第二个参数为优先级,数字越小优先级越高。当两个任务同时要执行时,优先级高的先执行。但需要注意的是,如果你这样写: + +```python +import sched + +def do_work(name): + print(f'你好:{name}') + +sch = sched.scheduler() +sch.enter(5, 2, do_work, argument=('python', )) +sch.enter(5, 1, do_work, argument=('iswbm', )) +sch.run() +``` + +那么先打印出来的是`你好:python` + +![](http://image.iswbm.com/20210521215257.png) + +为什么这里优先级失效了?1的优先级大于2,应该先运行下面的才对啊。 + +这是由于,只有当两个任务同时运行的时候,才会去检查优先级。如果两个任务触发的时间一前一后,那么还轮不到比较优先级。由于延迟队列的`延迟`是相对于当前运行这一行代码的时间来计算的,后一行代码比前一行代码晚了几毫秒,所以实际上产品经理这一行会先到时间,所以就会先运行。 + +为了使用绝对的精确时间,我们可以使用另外一个方法: + +```python +import sched +import time +import datetime + +def do_work(name): + print(f'你好:{name}') + +sch = sched.scheduler(time.time, time.sleep) +start_time = datetime.datetime.now() + datetime.timedelta(seconds=10) +start_time_ts = start_time.timestamp() +sch.enterabs(start_time_ts, 2, do_work, argument=('python', )) +sch.enterabs(start_time_ts, 1, do_work, argument=('iswbm', )) +sch.run() +``` + +运行效果如下图所示: + +![](http://image.iswbm.com/20210521215402.png) + +`sch.enterabs()`的第一个参数是任务开始时间的时间戳,这是一个绝对时间,这个时间可以使用datetime模块来生成,或者其他你熟悉的方式。后面的参数和`sch.enter()`完全一样。 + +如果你要运行的函数带有多个参数或者默认参数,那么可以使用下面的方式传入参数: + +```python +import sched +import time +import datetime + +def do_work(name, place, work='写代码'): + print(f'你好:{name},你在:{place}{work}') + +sch = sched.scheduler(time.time, time.sleep) +start_time = datetime.datetime.now() + datetime.timedelta(seconds=10) +start_time_ts = start_time.timestamp() +sch.enter(5, 2, do_work, argument=('产品经理', '杭州'), kwargs={'work': '写需求文档'}) +sch.enterabs(start_time_ts, 1, do_work, argument=('开发人员', '产品经理旁边'), kwargs={'work': '看着她'}) +sch.run() +``` + +argument参数对应的元组存放普通参数,kwargs对应的字典存放带参数名的参数。 + +本文来源于:公众号"未闻Code",作者:kingname + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst new file mode 100644 index 0000000..ca97650 --- /dev/null +++ b/source/c07/c07_12.rst @@ -0,0 +1,101 @@ +7.12 非常好用的调度模块 +======================= + +|image0| + +Python +自带一个调度器模块\ ``sched``\ ,它能为你实现优先级队列/延迟队列和定时队列。 + +这个模块的使用非常简单,首先以延迟队列为例: + +.. code:: python + + import sched + + def do_work(name): + print(f'你好:{name}') + + sch = sched.scheduler() + sch.enter(5, 1, do_work, argument=('iswbm', )) + sch.run() + +代码运行以后,会卡在\ ``sch.run()``\ 这里,5秒钟以后执行\ ``do_work('iswbm')``\ ,运行效果如下图所示: + +|image1| + +其中,\ ``sch.enter()``\ 的第一个参数为延迟的时间,单位为秒,第二个参数为优先级,数字越小优先级越高。当两个任务同时要执行时,优先级高的先执行。但需要注意的是,如果你这样写: + +.. code:: python + + import sched + + def do_work(name): + print(f'你好:{name}') + + sch = sched.scheduler() + sch.enter(5, 2, do_work, argument=('python', )) + sch.enter(5, 1, do_work, argument=('iswbm', )) + sch.run() + +那么先打印出来的是\ ``你好:python`` + +|image2| + +为什么这里优先级失效了?1的优先级大于2,应该先运行下面的才对啊。 + +这是由于,只有当两个任务同时运行的时候,才会去检查优先级。如果两个任务触发的时间一前一后,那么还轮不到比较优先级。由于延迟队列的\ ``延迟``\ 是相对于当前运行这一行代码的时间来计算的,后一行代码比前一行代码晚了几毫秒,所以实际上产品经理这一行会先到时间,所以就会先运行。 + +为了使用绝对的精确时间,我们可以使用另外一个方法: + +.. code:: python + + import sched + import time + import datetime + + def do_work(name): + print(f'你好:{name}') + + sch = sched.scheduler(time.time, time.sleep) + start_time = datetime.datetime.now() + datetime.timedelta(seconds=10) + start_time_ts = start_time.timestamp() + sch.enterabs(start_time_ts, 2, do_work, argument=('python', )) + sch.enterabs(start_time_ts, 1, do_work, argument=('iswbm', )) + sch.run() + +运行效果如下图所示: + +|image3| + +``sch.enterabs()``\ 的第一个参数是任务开始时间的时间戳,这是一个绝对时间,这个时间可以使用datetime模块来生成,或者其他你熟悉的方式。后面的参数和\ ``sch.enter()``\ 完全一样。 + +如果你要运行的函数带有多个参数或者默认参数,那么可以使用下面的方式传入参数: + +.. code:: python + + import sched + import time + import datetime + + def do_work(name, place, work='写代码'): + print(f'你好:{name},你在:{place}{work}') + + sch = sched.scheduler(time.time, time.sleep) + start_time = datetime.datetime.now() + datetime.timedelta(seconds=10) + start_time_ts = start_time.timestamp() + sch.enter(5, 2, do_work, argument=('产品经理', '杭州'), kwargs={'work': '写需求文档'}) + sch.enterabs(start_time_ts, 1, do_work, argument=('开发人员', '产品经理旁边'), kwargs={'work': '看着她'}) + sch.run() + +argument参数对应的元组存放普通参数,kwargs对应的字典存放带参数名的参数。 + +本文来源于:公众号“未闻Code”,作者:kingname + +|image4| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20210521215217.png +.. |image2| image:: http://image.iswbm.com/20210521215257.png +.. |image3| image:: http://image.iswbm.com/20210521215402.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c07/c07_13.md b/source/c07/c07_13.md new file mode 100644 index 0000000..dd8d3bf --- /dev/null +++ b/source/c07/c07_13.md @@ -0,0 +1,212 @@ +# 7.13 实现字典的点式操作 + +![](http://image.iswbm.com/20200804124133.png) + +字典是 Python 中基础的数据结构之一,字典的使用,可以说是非常的简单粗暴,但即便是这样一个与世无争的数据结构,仍然有很多人 "看不惯它" 。 + +也许你并不觉得,但我相信,你看了这篇文章后,一定会和我一样,对原生字典开始有了偏见。 + +我举个简单的例子吧 + +当你想访问字典中的某个 key 时,你需要使用字典特定的访问方式,而这种方式需要你键入 一对中括号 还有 一对引号 + +```python +>>> profile = dict(name="iswbm") +>>> profile +{'name': 'iswbm'} +>>> profile["name"] +'iswbm' +``` + +是不是开始觉得忍无可忍了? + +如果可以像调用对象属性一样使用 `.` 去访问 key 就好了,可以省去很多多余的键盘击入,就像这样子 + +```python +>>> profile.name +'iswbm' +``` + +是的,今天这篇文章就是跟大家分享一种可以直接使用 `.` 访问和操作字典的一个黑魔法库 -- `munch`。 + +## 1. 安装方法 + +使用如下命令进行安装 + +```shell +$ python -m pip install munch +``` + +## 2. 简单示例 + +munch 有一个 Munch 类,它继承自原生字典,使用 isinstance 可以验证 + +```python +>>> from munch import Munch +>>> profile = Munch() +>>> isinstance(profile, dict) +True +>>> +``` + +并实现了点式赋值与访问,`profile.name` 与 `profile['name']` 是等价的 + +```python +>>> profile.name = "iswbm" +>>> profile.age = 18 +>>> profile +Munch({'name': 'iswbm', 'age': 18}) +>>> +>>> profile.name +'iswbm' +>>> profile["name"] +'iswbm' +``` + +## 3. 兼容字典的所有操作 + +本身 Munch 继承自 dict,dict 的操作也同样适用于 Munch 对象,不妨再来验证下 + +首先是:增删改查 + +```python +# 新增元素 +>>> profile["gender"] = "male" +>>> profile +Munch({'name': 'iswbm', 'age': 18, 'gender': 'male'}) + +# 修改元素 +>>> profile["gender"] = "female" +>>> profile +Munch({'name': 'iswbm', 'age': 18, 'gender': 'female'}) + +# 删除元素 +>>> profile.pop("gender") +'female' +>>> profile +Munch({'name': 'iswbm', 'age': 18}) +>>> +>>> del profile["age"] +>>> profile +Munch({'name': 'iswbm'}) +``` + +再者是:一些常用方法 + +```python +>>> profile.keys() +dict_keys(['name']) +>>> +>>> profile.values() +dict_values(['iswbm']) +>>> +>>> profile.get('name') +'iswbm' +>>> profile.setdefault('gender', 'male') +'male' +>>> profile +Munch({'name': 'iswbm', 'gender': 'male'}) +``` + +## 4. 设置返回默认值 + +当访问一个字典中不存在的 key 时,会报 KeyError 的错误 + +```python +>>> profile = {} +>>> profile["name"] +Traceback (most recent call last): + File "", line 1, in +KeyError: 'name' +``` + +对于这种情况,通常我们会使用 get 来规避 + +```python +>>> profile = {} +>>> profile.get("name", "undefined") +'undefined' +``` + +当然你在 munch 中仍然可以这么用,不过还有一种更好的方法:使用 DefaultMunch,它会在你访问不存在的 key 时,给你返回一个设定好的默认值 + +```python +>>> from munch import DefaultMunch +>>> profile = DefaultMunch("undefined", {"name": "iswbm"}) +>>> profile +DefaultMunch('undefined', {'name': 'iswbm'}) +>>> profile.age +'undefined' +>>> profile +DefaultMunch('undefined', {'name': 'iswbm'}) +``` + +## 5. 工厂函数自动创建key + +上面使用 `DefaultMunch ` 仅当你访问不存在的 key 是返回一个默认值,但这个行为并不会修改原 munch 对象的任何内容。 + +若你想访问不存在的 key 时,自动触发给原 munch 中新增你想要访问的 key ,并为其设置一个默认值,可以试一下 `DefaultFactoryMunch` 传入一个工厂函数。 + +```python +>>> from munch import DefaultFactoryMunch +>>> profile = DefaultFactoryMunch(list, name='iswbm') +>>> profile +DefaultFactoryMunch(list, {'name': 'iswbm'}) +>>> +>>> profile.brothers +[] +>>> profile +DefaultFactoryMunch(list, {'name': 'iswbm', 'brothers': []}) +``` + +## 6. 序列化的支持 + +Munch 支持序列化为 JSON 或者 YAML 格式的字符串对象 + +**转换成 JSON** + +```python +>>> from munch import Munch +>>> munch_obj = Munch(foo=Munch(lol=True), bar=100, msg='hello') +>>> +>>> import json +>>> json.dumps(munch_obj) +'{"foo": {"lol": true}, "bar": 100, "msg": "hello"}' +``` + +**转换成 YAML** + +```python +>>> from munch import Munch +>>> munch_obj = Munch(foo=Munch(lol=True), bar=100, msg='hello') +>>> import yaml +>>> yaml.dump(munch_obj) +'!munch.Munch\nbar: 100\nfoo: !munch.Munch\n lol: true\nmsg: hello\n' +>>> +>>> print(yaml.dump(munch_obj)) +!munch.Munch +bar: 100 +foo: !munch.Munch + lol: true +msg: hello + +>>> +``` + +建议使用 `safe_dump` 去掉 `!munch.Munch` + +```python +>>> print(yaml.safe_dump(munch_obj)) +bar: 100 +foo: + lol: true +msg: hello +``` + + + +以上就是关于 munch 的使用全解,替换原生字典绝无问题,munch 的进一步封装使得数据的访问及操作更得更加 Pythonic 了,希望有一天这个特性能够体现在原生的字典上。 + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst new file mode 100644 index 0000000..beb81ad --- /dev/null +++ b/source/c07/c07_13.rst @@ -0,0 +1,233 @@ +7.13 实现字典的点式操作 +======================= + +|image0| + +字典是 Python +中基础的数据结构之一,字典的使用,可以说是非常的简单粗暴,但即便是这样一个与世无争的数据结构,仍然有很多人 +“看不惯它” 。 + +也许你并不觉得,但我相信,你看了这篇文章后,一定会和我一样,对原生字典开始有了偏见。 + +我举个简单的例子吧 + +当你想访问字典中的某个 key +时,你需要使用字典特定的访问方式,而这种方式需要你键入 一对中括号 还有 +一对引号 + +.. code:: python + + >>> profile = dict(name="iswbm") + >>> profile + {'name': 'iswbm'} + >>> profile["name"] + 'iswbm' + +是不是开始觉得忍无可忍了? + +如果可以像调用对象属性一样使用 ``.`` 去访问 key +就好了,可以省去很多多余的键盘击入,就像这样子 + +.. code:: python + + >>> profile.name + 'iswbm' + +是的,今天这篇文章就是跟大家分享一种可以直接使用 ``.`` +访问和操作字典的一个黑魔法库 – ``munch``\ 。 + +1. 安装方法 +----------- + +使用如下命令进行安装 + +.. code:: shell + + $ python -m pip install munch + +2. 简单示例 +----------- + +munch 有一个 Munch 类,它继承自原生字典,使用 isinstance 可以验证 + +.. code:: python + + >>> from munch import Munch + >>> profile = Munch() + >>> isinstance(profile, dict) + True + >>> + +并实现了点式赋值与访问,\ ``profile.name`` 与 ``profile['name']`` +是等价的 + +.. code:: python + + >>> profile.name = "iswbm" + >>> profile.age = 18 + >>> profile + Munch({'name': 'iswbm', 'age': 18}) + >>> + >>> profile.name + 'iswbm' + >>> profile["name"] + 'iswbm' + +3. 兼容字典的所有操作 +--------------------- + +本身 Munch 继承自 dict,dict 的操作也同样适用于 Munch +对象,不妨再来验证下 + +首先是:增删改查 + +.. code:: python + + # 新增元素 + >>> profile["gender"] = "male" + >>> profile + Munch({'name': 'iswbm', 'age': 18, 'gender': 'male'}) + + # 修改元素 + >>> profile["gender"] = "female" + >>> profile + Munch({'name': 'iswbm', 'age': 18, 'gender': 'female'}) + + # 删除元素 + >>> profile.pop("gender") + 'female' + >>> profile + Munch({'name': 'iswbm', 'age': 18}) + >>> + >>> del profile["age"] + >>> profile + Munch({'name': 'iswbm'}) + +再者是:一些常用方法 + +.. code:: python + + >>> profile.keys() + dict_keys(['name']) + >>> + >>> profile.values() + dict_values(['iswbm']) + >>> + >>> profile.get('name') + 'iswbm' + >>> profile.setdefault('gender', 'male') + 'male' + >>> profile + Munch({'name': 'iswbm', 'gender': 'male'}) + +4. 设置返回默认值 +----------------- + +当访问一个字典中不存在的 key 时,会报 KeyError 的错误 + +.. code:: python + + >>> profile = {} + >>> profile["name"] + Traceback (most recent call last): + File "", line 1, in + KeyError: 'name' + +对于这种情况,通常我们会使用 get 来规避 + +.. code:: python + + >>> profile = {} + >>> profile.get("name", "undefined") + 'undefined' + +当然你在 munch 中仍然可以这么用,不过还有一种更好的方法:使用 +DefaultMunch,它会在你访问不存在的 key 时,给你返回一个设定好的默认值 + +.. code:: python + + >>> from munch import DefaultMunch + >>> profile = DefaultMunch("undefined", {"name": "iswbm"}) + >>> profile + DefaultMunch('undefined', {'name': 'iswbm'}) + >>> profile.age + 'undefined' + >>> profile + DefaultMunch('undefined', {'name': 'iswbm'}) + +5. 工厂函数自动创建key +---------------------- + +上面使用 ``DefaultMunch`` 仅当你访问不存在的 key +是返回一个默认值,但这个行为并不会修改原 munch 对象的任何内容。 + +若你想访问不存在的 key 时,自动触发给原 munch 中新增你想要访问的 key +,并为其设置一个默认值,可以试一下 ``DefaultFactoryMunch`` +传入一个工厂函数。 + +.. code:: python + + >>> from munch import DefaultFactoryMunch + >>> profile = DefaultFactoryMunch(list, name='iswbm') + >>> profile + DefaultFactoryMunch(list, {'name': 'iswbm'}) + >>> + >>> profile.brothers + [] + >>> profile + DefaultFactoryMunch(list, {'name': 'iswbm', 'brothers': []}) + +6. 序列化的支持 +--------------- + +Munch 支持序列化为 JSON 或者 YAML 格式的字符串对象 + +**转换成 JSON** + +.. code:: python + + >>> from munch import Munch + >>> munch_obj = Munch(foo=Munch(lol=True), bar=100, msg='hello') + >>> + >>> import json + >>> json.dumps(munch_obj) + '{"foo": {"lol": true}, "bar": 100, "msg": "hello"}' + +**转换成 YAML** + +.. code:: python + + >>> from munch import Munch + >>> munch_obj = Munch(foo=Munch(lol=True), bar=100, msg='hello') + >>> import yaml + >>> yaml.dump(munch_obj) + '!munch.Munch\nbar: 100\nfoo: !munch.Munch\n lol: true\nmsg: hello\n' + >>> + >>> print(yaml.dump(munch_obj)) + !munch.Munch + bar: 100 + foo: !munch.Munch + lol: true + msg: hello + + >>> + +建议使用 ``safe_dump`` 去掉 ``!munch.Munch`` + +.. code:: python + + >>> print(yaml.safe_dump(munch_obj)) + bar: 100 + foo: + lol: true + msg: hello + +以上就是关于 munch 的使用全解,替换原生字典绝无问题,munch +的进一步封装使得数据的访问及操作更得更加 Pythonic +了,希望有一天这个特性能够体现在原生的字典上。 + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200804124133.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png +