标签归档:Python

Mitmproxy-面向渗透测试人员和软件开发人员的交互式TLS拦截HTTP代理

mitmproxy是一个交互式的、支持SSL/TLS的拦截代理,具有HTTP/1、HTTP/2和WebSocket的控制台接口

mitmdump是Mitmproxy的命令行版本。考虑HTTP的tcpdump

mitmweb是Mitmproxy的基于web的界面。

安装

安装说明如下here如果要从源代码安装,请参见CONTRIBUTING.md

文档和帮助

一般信息、教程和预编译的二进制文件可以在Mitmproxy网站上找到

mitmproxy.org

Mitmproxy的文档可在我们的网站上找到:

mitmproxy documentation stable
mitmproxy documentation dev

如果您有关于如何使用Mitmproxy的问题,请在StackOverflow上询问他们!

StackOverflow: mitmproxy

贡献

作为一个开源项目,Mitmproxy欢迎各种形式的贡献

Dev Guide

另外,请随时加入我们的开发者松弛!

Slack Developer Chat

Compose-使用Docker定义和运行多容器应用程序

Docker Compose是一个用于在Docker上运行多容器应用程序的工具,该工具使用Compose file format合成文件用于定义如何配置组成应用程序的一个或多个容器。一旦您有了合成文件,您就可以使用单个命令创建和启动您的应用程序:docker-compose up

合成文件可用于在本地部署应用程序,或将应用程序部署到云中Amazon ECSMicrosoft ACI使用Docker CLI。您可以阅读有关如何执行此操作的更多信息:

在哪里可以找到Docker Compose

Windows和MacOS

Docker Compose包含在Docker Desktop对于Windows和MacOS

Linux操作系统

您可以从下载Docker Compose二进制文件release page在此存储库上

使用管道

如果您的平台不受支持,您可以使用下载Docker Composepip

pip install docker-compose

注:Docker Compose需要Python 3.6或更高版本

快速入门

使用Docker Compose基本上有三个步骤:

  1. 使用定义您的应用程序的环境Dockerfile所以它可以在任何地方复制
  2. 在中定义构成应用程序的服务docker-compose.yml因此它们可以在隔离环境中一起运行
  3. 最后,运行docker-compose upCompose将启动并运行您的整个应用程序

合成文件如下所示:

services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
  redis:
    image: redis

您可以在我们的Awesome Compose repository

有关合成格式的更多信息,请参见Compose file reference

贡献

想要帮助开发Docker作曲吗?请查看我们的contributing documentation

如果您发现问题,请在issue tracker

释放

发行版是由维护人员构建的,遵循release process

Handson-ml-Jupyter 引导您学习机器学习和深度学习的基础知识

Handson-ml-Jupyter 使用Scikit-Learning和TensorFlow在Python中引导您学习机器学习和深度学习的基础知识。

机器学习笔记本

本项目旨在教您Python中机器学习的基础知识。它包含我的O‘Reilly书中练习的示例代码和解决方案Hands-on Machine Learning with Scikit-Learn and TensorFlow

book

警告:这本书现在有一个较新的版本,请查收github.com/ageron/handson-ml2

快速入门

想在不安装任何东西的情况下在线玩这些笔记本吗?

使用以下任一服务

警告:请注意,这些服务提供临时环境:您所做的任何操作都将在一段时间后被删除,因此请确保下载您关心的任何数据

  • 推荐:在以下位置打开此存储库Colaboratory
  • 或在中打开它Binder
    • 注意事项:大多数情况下,Binder启动速度很快,工作效果很好,但是当handson-ml更新时,Binder会从头开始创建一个新环境,这可能需要相当长的时间
  • 或在中打开它Deepnote

只想快速浏览一些笔记本,而不执行任何代码?

使用浏览此存储库jupyter.org’s notebook viewer

注意事项github.com’s notebook viewer也可以工作,但速度较慢,并且数学公式并不总是正确显示

要使用Docker映像运行此项目吗?

请阅读Docker instructions

要在您自己的计算机上安装此项目吗?

从安装开始Anaconda(或Miniconda),git,如果您有兼容TensorFlow的GPU,请安装GPU driver,以及相应版本的CUDA和cuDNN(有关详细信息,请参阅TensorFlow的文档)

接下来,通过打开终端并键入以下命令(不要键入第一个命令)来克隆此项目$每行上的符号仅表示这些是终端命令):

$ git clone https://github.com/ageron/handson-ml.git
$ cd handson-ml

接下来,运行以下命令:

$ conda env create -f environment.yml
$ conda activate tf1
$ python -m ipykernel install --user --name=python3

最后,启动Jupyter:

$ jupyter notebook

如果您需要进一步的说明,请阅读detailed installation instructions

常见问题解答

我应该使用哪个Python版本?

我推荐Python3.7。如果您按照上面的安装说明操作,您将获得该版本。大多数代码都可以与其他版本的Python3一起使用,但有些库还不支持Python3.8或3.9,这就是我推荐Python3.7的原因

当我调用时收到错误消息load_housing_data()

一定要给我打电话fetch_housing_data()在此之前你打电话给我load_housing_data()如果您收到HTTP错误,请确保您运行的代码与笔记本中的代码完全相同(如果需要,请复制/粘贴)。如果问题仍然存在,请检查您的网络配置

我在MacOSX上收到SSL错误

您可能需要安装SSL证书(请参阅此处StackOverflow question)。如果您从官方网站下载了Python,则运行/Applications/Python\ 3.7/Install\ Certificates.command在终端中(更改3.7到您安装的任何版本)。如果使用MacPorts安装Python,请运行sudo port install curl-ca-bundle在终端中

我已经在本地安装了这个项目。如何将其更新到最新版本?

看见INSTALL.md

在使用python时,如何将我的Python库更新到最新版本?

看见INSTALL.md

贡献者

我要感谢所有人who contributed to this project,通过提供有用的反馈、提交问题或提交拉取请求。特别感谢海森·帕克和伊恩·博雷德,他们审阅了每个笔记本,并提交了许多公关,包括在一些练习解决方案上的帮助。还要感谢史蒂文·邦克利和齐恩布拉,他们创造了docker目录,并感谢GitHub用户SuperYorio,他在一些运动解决方案上提供了帮助

Python-guide Python最佳实践指南,为人类编写

Python搭便车指南

Python最佳实践指南

→在以下网址阅读免费指南:docs.python-guide.org

https://farm1.staticflickr.com/628/33173824932_58add34581_k_d.jpg


工作正在进行中。如果你愿意帮忙,请便。有很多工作要做

这本指南目前正在大力开发中。本自以为是的指南旨在为Python新手和专业开发人员提供每日安装、配置和使用Python的最佳实践手册

主题包括:

  • 特定于平台和版本的安装
  • Py2app、Py2exe、bbFreeze、pyInstaller
  • 管道
  • 虚拟环境
  • 交换矩阵
  • 详尽的模块建议,按主题/目的分组
  • 哪些库用于什么用途
  • 适用于各种Web框架的服务器配置和工具
  • 文档:编写文档
  • 测试:Jenkins&Tox指南
  • 如何轻松实现界面hg从…git

如果你不喜欢阅读reStructiredText,这里有一个几乎是最新的HTML version at docs.python-guide.org

Python-cheatsheet Python 各种操作的示例小抄

目录

*1.数据结构: ListDictionarySetTupleRangeEnumerateIteratorGenerator
*2.类型:*TypeStringRegular_ExpFormatNumbersCombinatoricsDatetime
*3.语法:*ArgsInlineClosureDecoratorClassDuck_TypeEnumException
*4.系统:*ExitPrintInputCommand_Line_ArgumentsOpenPathOS_Commands
*5.数据:*JSONPickleCSVSQLiteBytesStructArrayMemory_ViewDeque
*6.高级:*ThreadingOperatorIntrospectionMetaprogramingEvalCoroutines
*7.模块:*Progress_BarPlotTableCursesLoggingScrapingWebProfile
*NumPyImageAudioGamesData

Main

if __name__ == '__main__':     # Runs main() if file wasn't imported.
    main()

列表

<list> = <list>[from_inclusive : to_exclusive : ±step_size]

<list>.append(<el>)            # Or: <list> += [<el>]
<list>.extend(<collection>)    # Or: <list> += <collection>

<list>.sort()
<list>.reverse()
<list> = sorted(<collection>)
<iter> = reversed(<list>)

sum_of_elements  = sum(<collection>)
elementwise_sum  = [sum(pair) for pair in zip(list_a, list_b)]
sorted_by_second = sorted(<collection>, key=lambda el: el[1])
sorted_by_both   = sorted(<collection>, key=lambda el: (el[1], el[0]))
flatter_list     = list(itertools.chain.from_iterable(<list>))
product_of_elems = functools.reduce(lambda out, el: out * el, <collection>)
list_of_chars    = list(<str>)
  • 模块operator提供函数itemgetter()和mul(),它们提供的功能与lambda上面的表达式

<list>.insert(<int>, <el>)     # Inserts item at index and moves the rest to the right.
<el>  = <list>.pop([<int>])    # Returns and removes item at index or from the end.
<int> = <list>.count(<el>)     # Returns number of occurrences. Also works on strings.
<int> = <list>.index(<el>)     # Returns index of the first occurrence or raises ValueError.
<list>.remove(<el>)            # Removes first occurrence of the item or raises ValueError.
<list>.clear()                 # Removes all items. Also works on dictionary and set.

词典

<view> = <dict>.keys()                          # Coll. of keys that reflects changes.
<view> = <dict>.values()                        # Coll. of values that reflects changes.
<view> = <dict>.items()                         # Coll. of key-value tuples that reflects chgs.

value  = <dict>.get(key, default=None)          # Returns default if key is missing.
value  = <dict>.setdefault(key, default=None)   # Returns and writes default if key is missing.
<dict> = collections.defaultdict(<type>)        # Creates a dict with default value of type.
<dict> = collections.defaultdict(lambda: 1)     # Creates a dict with default value 1.

<dict> = dict(<collection>)                     # Creates a dict from coll. of key-value pairs.
<dict> = dict(zip(keys, values))                # Creates a dict from two collections.
<dict> = dict.fromkeys(keys [, value])          # Creates a dict from collection of keys.

<dict>.update(<dict>)                           # Adds items. Replaces ones with matching keys.
value = <dict>.pop(key)                         # Removes item or raises KeyError.
{k for k, v in <dict>.items() if v == value}    # Returns set of keys that point to the value.
{k: v for k, v in <dict>.items() if k in keys}  # Returns a dictionary, filtered by keys.

计数器

>>> from collections import Counter
>>> colors = ['blue', 'blue', 'blue', 'red', 'red']
>>> counter = Counter(colors)
>>> counter['yellow'] += 1
Counter({'blue': 3, 'red': 2, 'yellow': 1})
>>> counter.most_common()[0]
('blue', 3)

设置

<set> = set()

<set>.add(<el>)                                 # Or: <set> |= {<el>}
<set>.update(<collection> [, ...])              # Or: <set> |= <set>

<set>  = <set>.union(<coll.>)                   # Or: <set> | <set>
<set>  = <set>.intersection(<coll.>)            # Or: <set> & <set>
<set>  = <set>.difference(<coll.>)              # Or: <set> - <set>
<set>  = <set>.symmetric_difference(<coll.>)    # Or: <set> ^ <set>
<bool> = <set>.issubset(<coll.>)                # Or: <set> <= <set>
<bool> = <set>.issuperset(<coll.>)              # Or: <set> >= <set>

<el> = <set>.pop()                              # Raises KeyError if empty.
<set>.remove(<el>)                              # Raises KeyError if missing.
<set>.discard(<el>)                             # Doesn't raise an error.

冻结集

  • 是不可变的和可哈希的
  • 这意味着它可以用作字典中的键或集合中的元素

<frozenset> = frozenset(<collection>)

元组

元组是一个不可变且可散列的列表

<tuple> = ()
<tuple> = (<el>,)                           # Or: <el>,
<tuple> = (<el_1>, <el_2> [, ...])          # Or: <el_1>, <el_2> [, ...]

命名元组

具有命名元素的元组的子类

>>> from collections import namedtuple
>>> Point = namedtuple('Point', 'x y')
>>> p = Point(1, y=2)
Point(x=1, y=2)
>>> p[0]
1
>>> p.x
1
>>> getattr(p, 'y')
2
>>> p._fields  # Or: Point._fields
('x', 'y')

范围

<range> = range(to_exclusive)
<range> = range(from_inclusive, to_exclusive)
<range> = range(from_inclusive, to_exclusive, ±step_size)

from_inclusive = <range>.start
to_exclusive   = <range>.stop

枚举

for i, el in enumerate(<collection> [, i_start]):
    ...

迭代器

<iter> = iter(<collection>)                 # `iter(<iter>)` returns unmodified iterator.
<iter> = iter(<function>, to_exclusive)     # A sequence of return values until 'to_exclusive'.
<el>   = next(<iter> [, default])           # Raises StopIteration or returns 'default' on end.
<list> = list(<iter>)                       # Returns a list of iterator's remaining elements.

迭代工具

from itertools import count, repeat, cycle, chain, islice

<iter> = count(start=0, step=1)             # Returns updated value endlessly. Accepts floats.
<iter> = repeat(<el> [, times])             # Returns element endlessly or 'times' times.
<iter> = cycle(<collection>)                # Repeats the sequence endlessly.

<iter> = chain(<coll_1>, <coll_2> [, ...])  # Empties collections in order.
<iter> = chain.from_iterable(<collection>)  # Empties collections inside a collection in order.

<iter> = islice(<coll>, to_exclusive)       # Only returns first 'to_exclusive' elements.
<iter> = islice(<coll>, from_inclusive, …)  # `to_exclusive, step_size`.

发电机

  • 任何包含Year语句的函数都返回生成器
  • 生成器和迭代器是可互换的

def count(start, step):
    while True:
        yield start
        start += step

>>> counter = count(10, 2)
>>> next(counter), next(counter), next(counter)
(10, 12, 14)

类型

  • 一切都是对象
  • 每个对象都有一个类型
  • 类型和类是同义词

<type> = type(<el>)                          # Or: <el>.__class__
<bool> = isinstance(<el>, <type>)            # Or: issubclass(type(<el>), <type>)

>>> type('a'), 'a'.__class__, str
(<class 'str'>, <class 'str'>, <class 'str'>)

某些类型没有内置名称,因此必须导入:

from types import FunctionType, MethodType, LambdaType, GeneratorType

抽象基类

每个抽象基类指定一组虚拟子类。这些类随后被isinstance()和issubclass()识别为ABC的子类,尽管它们实际上不是。ABC还可以手动决定特定类是否为其虚拟子类,通常基于该类实现了哪些方法。例如,Iterable ABC查找方法ITER(),而集合ABC查找方法ITER()、CONTAINS()和len()

>>> from collections.abc import Sequence, Collection, Iterable
>>> isinstance([1, 2, 3], Iterable)
True

+------------------+------------+------------+------------+
|                  |  Sequence  | Collection |  Iterable  |
+------------------+------------+------------+------------+
| list, range, str |    yes     |    yes     |    yes     |
| dict, set        |            |    yes     |    yes     |
| iter             |            |            |    yes     |
+------------------+------------+------------+------------+

>>> from numbers import Integral, Rational, Real, Complex, Number
>>> isinstance(123, Number)
True

+--------------------+----------+----------+----------+----------+----------+
|                    | Integral | Rational |   Real   | Complex  |  Number  |
+--------------------+----------+----------+----------+----------+----------+
| int                |   yes    |   yes    |   yes    |   yes    |   yes    |
| fractions.Fraction |          |   yes    |   yes    |   yes    |   yes    |
| float              |          |          |   yes    |   yes    |   yes    |
| complex            |          |          |          |   yes    |   yes    |
| decimal.Decimal    |          |          |          |          |   yes    |
+--------------------+----------+----------+----------+----------+----------+

字符串

<str>  = <str>.strip()                       # Strips all whitespace characters from both ends.
<str>  = <str>.strip('<chars>')              # Strips all passed characters from both ends.

<list> = <str>.split()                       # Splits on one or more whitespace characters.
<list> = <str>.split(sep=None, maxsplit=-1)  # Splits on 'sep' str at most 'maxsplit' times.
<list> = <str>.splitlines(keepends=False)    # Splits on [\n\r\f\v\x1c\x1d\x1e\x85] and '\r\n'.
<str>  = <str>.join(<coll_of_strings>)       # Joins elements using string as a separator.

<bool> = <sub_str> in <str>                  # Checks if string contains a substring.
<bool> = <str>.startswith(<sub_str>)         # Pass tuple of strings for multiple options.
<bool> = <str>.endswith(<sub_str>)           # Pass tuple of strings for multiple options.
<int>  = <str>.find(<sub_str>)               # Returns start index of the first match or -1.
<int>  = <str>.index(<sub_str>)              # Same but raises ValueError if missing.

<str>  = <str>.replace(old, new [, count])   # Replaces 'old' with 'new' at most 'count' times.
<str>  = <str>.translate(<table>)            # Use `str.maketrans(<dict>)` to generate table.

<str>  = chr(<int>)                          # Converts int to Unicode char.
<int>  = ord(<str>)                          # Converts Unicode char to int.
  • 另外:'lstrip()''rstrip()'
  • 另外:'lower()''upper()''capitalize()''title()'

属性方法

+---------------+----------+----------+----------+----------+----------+
|               | [ !#$%…] | [a-zA-Z] |  [¼½¾]   |  [²³¹]   |  [0-9]   |
+---------------+----------+----------+----------+----------+----------+
| isprintable() |   yes    |   yes    |   yes    |   yes    |   yes    |
| isalnum()     |          |   yes    |   yes    |   yes    |   yes    |
| isnumeric()   |          |          |   yes    |   yes    |   yes    |
| isdigit()     |          |          |          |   yes    |   yes    |
| isdecimal()   |          |          |          |          |   yes    |
+---------------+----------+----------+----------+----------+----------+
  • 另外:'isspace()'检查'[ \t\n\r\f\v…]'

正则表达式

import re
<str>   = re.sub(<regex>, new, text, count=0)  # Substitutes all occurrences with 'new'.
<list>  = re.findall(<regex>, text)            # Returns all occurrences as strings.
<list>  = re.split(<regex>, text, maxsplit=0)  # Use brackets in regex to include the matches.
<Match> = re.search(<regex>, text)             # Searches for first occurrence of the pattern.
<Match> = re.match(<regex>, text)              # Searches only at the beginning of the text.
<iter>  = re.finditer(<regex>, text)           # Returns all occurrences as match objects.
  • 如果Search()和Match()找不到匹配项,则返回NONE
  • 论据'flags=re.IGNORECASE'可以与所有功能一起使用
  • 论据'flags=re.MULTILINE'使'^''$'匹配每行的开始/结束
  • 论据'flags=re.DOTALL'使点也接受'\n'
  • 使用r'\1''\\1'用于反向引用
  • 添加'?'在操作员之后,使其不贪婪

匹配对象

<str>   = <Match>.group()                      # Returns the whole match. Also group(0).
<str>   = <Match>.group(1)                     # Returns part in the first bracket.
<tuple> = <Match>.groups()                     # Returns all bracketed parts.
<int>   = <Match>.start()                      # Returns start index of the match.
<int>   = <Match>.end()                        # Returns exclusive end index of the match.

特殊序列

  • 默认情况下,匹配所有字母表中的十进制字符、字母数字和空格,除非'flags=re.ASCII'参数被使用
  • 如下所示,它将特殊序列匹配限制为前128个字符,并防止'\s'从接受'[\x1c-\x1f]'
  • 用大写字母表示否定

'\d' == '[0-9]'                                # Matches decimal characters.
'\w' == '[a-zA-Z0-9_]'                         # Matches alphanumerics and underscore.
'\s' == '[ \t\n\r\f\v]'                        # Matches whitespaces.

格式化

<str> = f'{<el_1>}, {<el_2>}'
<str> = '{}, {}'.format(<el_1>, <el_2>)

属性

>>> from collections import namedtuple
>>> Person = namedtuple('Person', 'name height')
>>> person = Person('Jean-Luc', 187)
>>> f'{person.height}'
'187'
>>> '{p.height}'.format(p=person)
'187'

常规选项

{<el>:<10}                                     # '<el>      '
{<el>:^10}                                     # '   <el>   '
{<el>:>10}                                     # '      <el>'
{<el>:.<10}                                    # '<el>......'
{<el>:0}                                       # '<el>'
  • 使用'{<el>:{<str/int/float>}[...]}'要动态设置选项,请执行以下操作
  • 添加'!r'在冒号通过调用其repr()方法

字符串

{'abcde'!r:10}                                 # "'abcde'   "
{'abcde':10.3}                                 # 'abc       '
{'abcde':.3}                                   # 'abc'

数字

{ 123456:10,}                                  # '   123,456'
{ 123456:10_}                                  # '   123_456'
{ 123456:+10}                                  # '   +123456'
{-123456:=10}                                  # '-   123456'
{ 123456: }                                    # ' 123456'
{-123456: }                                    # '-123456'

浮动车

{1.23456:10.3}                                 # '      1.23'
{1.23456:10.3f}                                # '     1.235'
{1.23456:10.3e}                                # ' 1.235e+00'
{1.23456:10.3%}                                # '  123.456%'

演示文稿类型比较:

+--------------+----------------+----------------+----------------+----------------+
|              |    {<float>}   |   {<float>:f}  |   {<float>:e}  |   {<float>:%}  |
+--------------+----------------+----------------+----------------+----------------+
|  0.000056789 |   '5.6789e-05' |    '0.000057'  | '5.678900e-05' |    '0.005679%' |
|  0.00056789  |   '0.00056789' |    '0.000568'  | '5.678900e-04' |    '0.056789%' |
|  0.0056789   |   '0.0056789'  |    '0.005679'  | '5.678900e-03' |    '0.567890%' |
|  0.056789    |   '0.056789'   |    '0.056789'  | '5.678900e-02' |    '5.678900%' |
|  0.56789     |   '0.56789'    |    '0.567890'  | '5.678900e-01' |   '56.789000%' |
|  5.6789      |   '5.6789'     |    '5.678900'  | '5.678900e+00' |  '567.890000%' |
| 56.789       |  '56.789'      |   '56.789000'  | '5.678900e+01' | '5678.900000%' |
+--------------+----------------+----------------+----------------+----------------+

+--------------+----------------+----------------+----------------+----------------+
|              |  {<float>:.2}  |  {<float>:.2f} |  {<float>:.2e} |  {<float>:.2%} |
+--------------+----------------+----------------+----------------+----------------+
|  0.000056789 |    '5.7e-05'   |      '0.00'    |   '5.68e-05'   |      '0.01%'   |
|  0.00056789  |    '0.00057'   |      '0.00'    |   '5.68e-04'   |      '0.06%'   |
|  0.0056789   |    '0.0057'    |      '0.01'    |   '5.68e-03'   |      '0.57%'   |
|  0.056789    |    '0.057'     |      '0.06'    |   '5.68e-02'   |      '5.68%'   |
|  0.56789     |    '0.57'      |      '0.57'    |   '5.68e-01'   |     '56.79%'   |
|  5.6789      |    '5.7'       |      '5.68'    |   '5.68e+00'   |    '567.89%'   |
| 56.789       |    '5.7e+01'   |     '56.79'    |   '5.68e+01'   |   '5678.90%'   |
+--------------+----------------+----------------+----------------+----------------+
  • 当向上舍入和向下舍入都可以时,将选择返回结果的最后一个数字为偶数位的结果。这使得'{6.5:.0f}'一个'6''{7.5:.0f}'一个'8'

INTS

{90:c}                                   # 'Z'
{90:b}                                   # '1011010'
{90:X}                                   # '5A'

数字

类型

<int>      = int(<float/str/bool>)       # Or: math.floor(<float>)
<float>    = float(<int/str/bool>)       # Or: <real>e±<int>
<complex>  = complex(real=0, imag=0)     # Or: <real> ± <real>j
<Fraction> = fractions.Fraction(0, 1)    # Or: Fraction(numerator=0, denominator=1)
<Decimal>  = decimal.Decimal(<str/int>)  # Or: Decimal((sign, digits, exponent))
  • 'int(<str>)''float(<str>)'对格式错误的字符串引发ValueError
  • 十进制数可以精确表示,这与浮点数不同'1.1 + 2.2 != 3.3'
  • 小数运算的精度通过以下方式设置:'decimal.getcontext().prec = <int>'

基本功能

<num> = pow(<num>, <num>)                # Or: <num> ** <num>
<num> = abs(<num>)                       # <float> = abs(<complex>)
<num> = round(<num> [, ±ndigits])        # `round(126, -1) == 130`

数学

from math import e, pi, inf, nan, isinf, isnan
from math import sin, cos, tan, asin, acos, atan, degrees, radians
from math import log, log10, log2

统计数据

from statistics import mean, median, variance, stdev, pvariance, pstdev

随机的

from random import random, randint, choice, shuffle, gauss, seed

<float> = random()                       # A float inside [0, 1).
<int>   = randint(from_inc, to_inc)      # An int inside [from_inc, to_inc].
<el>    = choice(<list>)                 # Keeps the list intact.

宾,祸不单行

<int> = ±0b<bin>                         # Or: ±0x<hex>
<int> = int('±<bin>', 2)                 # Or: int('±<hex>', 16)
<int> = int('±0b<bin>', 0)               # Or: int('±0x<hex>', 0)
<str> = bin(<int>)                       # Returns '[-]0b<bin>'.

位运算符

<int> = <int> & <int>                    # And
<int> = <int> | <int>                    # Or
<int> = <int> ^ <int>                    # Xor (0 if both bits equal)
<int> = <int> << n_bits                  # Left shift (>> for right)
<int> = ~<int>                           # Not (also: -<int> - 1)

组合学

  • 每个函数都返回一个迭代器
  • 如果要打印迭代器,则需要首先将其传递给list()函数!

from itertools import product, combinations, combinations_with_replacement, permutations

>>> product([0, 1], repeat=3)
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), ..., (1, 1, 1)]

>>> product('abc', 'abc')                    #   a  b  c
[('a', 'a'), ('a', 'b'), ('a', 'c'),         # a x  x  x
 ('b', 'a'), ('b', 'b'), ('b', 'c'),         # b x  x  x
 ('c', 'a'), ('c', 'b'), ('c', 'c')]         # c x  x  x

>>> combinations('abc', 2)                   #   a  b  c
[('a', 'b'), ('a', 'c'),                     # a .  x  x
 ('b', 'c')]                                 # b .  .  x

>>> combinations_with_replacement('abc', 2)  #   a  b  c
[('a', 'a'), ('a', 'b'), ('a', 'c'),         # a x  x  x
 ('b', 'b'), ('b', 'c'),                     # b .  x  x
 ('c', 'c')]                                 # c .  .  x

>>> permutations('abc', 2)                   #   a  b  c
[('a', 'b'), ('a', 'c'),                     # a .  x  x
 ('b', 'a'), ('b', 'c'),                     # b x  .  x
 ('c', 'a'), ('c', 'b')]                     # c x  x  .

日期时间

  • 模块“DateTime”提供“Date”<D>,‘时间’<T>,‘DateTime’<DT>和“时间增量”<TD>上课。所有这些都是不可变的和可哈希的
  • Time和DateTime对象可以是“感知的”<a>,意味着他们已经定义了时区,或者说是“幼稚的”<n>,也就是说他们不会
  • 如果对象是朴素的,则假定它位于系统的时区

from datetime import date, time, datetime, timedelta
from dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginary

构造函数

<D>  = date(year, month, day)
<T>  = time(hour=0, minute=0, second=0, microsecond=0, tzinfo=None, fold=0)
<DT> = datetime(year, month, day, hour=0, minute=0, second=0, ...)
<TD> = timedelta(days=0, seconds=0, microseconds=0, milliseconds=0,
                 minutes=0, hours=0, weeks=0)
  • 使用'<D/DT>.weekday()'获取星期几(MON==0)
  • 'fold=1'表示时间倒退一小时的情况下的第二次传递
  • '<DTa> = resolve_imaginary(<DTa>)'修复了丢失小时内的DT

现在

<D/DTn>  = D/DT.today()                     # Current local date or naive datetime.
<DTn>    = DT.utcnow()                      # Naive datetime from current UTC time.
<DTa>    = DT.now(<tzinfo>)                 # Aware datetime from current tz time.
  • 提取时间使用情况'<DTn>.time()''<DTa>.time()''<DTa>.timetz()'

时区

<tzinfo> = UTC                              # UTC timezone. London without DST.
<tzinfo> = tzlocal()                        # Local timezone. Also gettz().
<tzinfo> = gettz('<Continent>/<City>')      # 'Continent/City_Name' timezone or None.
<DTa>    = <DT>.astimezone(<tzinfo>)        # Datetime, converted to the passed timezone.
<Ta/DTa> = <T/DT>.replace(tzinfo=<tzinfo>)  # Unconverted object with a new timezone.

编码

<D/T/DT> = D/T/DT.fromisoformat('<iso>')    # Object from ISO string. Raises ValueError.
<DT>     = DT.strptime(<str>, '<format>')   # Datetime from str, according to format.
<D/DTn>  = D/DT.fromordinal(<int>)          # D/DTn from days since the Gregorian NYE 1.
<DTn>    = DT.fromtimestamp(<real>)         # Local time DTn from seconds since the Epoch.
<DTa>    = DT.fromtimestamp(<real>, <tz.>)  # Aware datetime from seconds since the Epoch.
  • ISO字符串采用以下形式:'YYYY-MM-DD''HH:MM:SS.ffffff[±<offset>]',或两者均由任意字符分隔。偏移的格式为:'HH:MM'
  • Unix系统上的纪元是:'1970-01-01 00:00 UTC''1970-01-01 01:00 CET'

解码

<str>    = <D/T/DT>.isoformat(sep='T')      # Also timespec='auto/hours/minutes/seconds'.
<str>    = <D/T/DT>.strftime('<format>')    # Custom string representation.
<int>    = <D/DT>.toordinal()               # Days since Gregorian NYE 1, ignoring time and tz.
<float>  = <DTn>.timestamp()                # Seconds since the Epoch, from DTn in local tz.
<float>  = <DTa>.timestamp()                # Seconds since the Epoch, from DTa.

格式化

>>> from datetime import datetime
>>> dt = datetime.strptime('2015-05-14 23:39:00.00 +0200', '%Y-%m-%d %H:%M:%S.%f %z')
>>> dt.strftime("%A, %dth of %B '%y, %I:%M%p %Z")
"Thursday, 14th of May '15, 11:39PM UTC+02:00"
  • 在解析时,'%z'也接受'±HH:MM'
  • 用于缩写的工作日和月份使用'%a''%b'

算术

<D/DT>   = <D/DT>   ± <TD>                  # Returned datetime can fall into missing hour.
<TD>     = <D/DTn>  - <D/DTn>               # Returns the difference, ignoring time jumps.
<TD>     = <DTa>    - <DTa>                 # Ignores time jumps if they share tzinfo object.
<TD>     = <DT_UTC> - <DT_UTC>              # Convert DTs to UTC to get the actual delta.

论据

内部函数调用

<function>(<positional_args>)                  # f(0, 0)
<function>(<keyword_args>)                     # f(x=0, y=0)
<function>(<positional_args>, <keyword_args>)  # f(0, y=0)

内部函数定义

def f(<nondefault_args>):                      # def f(x, y):
def f(<default_args>):                         # def f(x=0, y=0):
def f(<nondefault_args>, <default_args>):      # def f(x, y=0):

Splat运算符

内部函数调用

Splat将集合扩展为位置参数,而Splatty-Splat将字典扩展为关键字参数

args   = (1, 2)
kwargs = {'x': 3, 'y': 4, 'z': 5}
func(*args, **kwargs)

等同于:

func(1, 2, x=3, y=4, z=5)

内部函数定义

Splat将零个或多个位置参数组合到一个元组中,而Splatty-Splat将零个或多个关键字参数组合到字典中

def add(*a):
    return sum(a)

>>> add(1, 2, 3)
6

法律论据组合:

def f(x, y, z):                # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
def f(*, x, y, z):             # f(x=1, y=2, z=3)
def f(x, *, y, z):             # f(x=1, y=2, z=3) | f(1, y=2, z=3)
def f(x, y, *, z):             # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3)

def f(*args):                  # f(1, 2, 3)
def f(x, *args):               # f(1, 2, 3)
def f(*args, z):               # f(1, 2, z=3)
def f(x, *args, z):            # f(1, 2, z=3)

def f(**kwargs):               # f(x=1, y=2, z=3)
def f(x, **kwargs):            # f(x=1, y=2, z=3) | f(1, y=2, z=3)
def f(*, x, **kwargs):         # f(x=1, y=2, z=3)

def f(*args, **kwargs):        # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
def f(x, *args, **kwargs):     # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
def f(*args, y, **kwargs):     # f(x=1, y=2, z=3) | f(1, y=2, z=3)
def f(x, *args, z, **kwargs):  # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3)

其他用途

<list> = [*<collection> [, ...]]
<set>  = {*<collection> [, ...]}
<tup.> = (*<collection>, [...])
<dict> = {**<dict> [, ...]}

head, *body, tail = <collection>

内联

兰姆达

<func> = lambda: <return_value>
<func> = lambda <arg_1>, <arg_2>: <return_value>

理解

<list> = [i+1 for i in range(10)]                         # [1, 2, ..., 10]
<set>  = {i for i in range(10) if i > 5}                  # {6, 7, 8, 9}
<iter> = (i+5 for i in range(10))                         # (5, 6, ..., 14)
<dict> = {i: i*2 for i in range(10)}                      # {0: 0, 1: 2, ..., 9: 18}

>>> [l+r for l in 'abc' for r in 'abc']
['aa', 'ab', 'ac', ..., 'cc']

地图、过滤、缩小

<iter> = map(lambda x: x + 1, range(10))                  # (1, 2, ..., 10)
<iter> = filter(lambda x: x > 5, range(10))               # (6, 7, 8, 9)
<obj>  = reduce(lambda out, x: out + x, range(10))        # 45
  • Reduce必须从functools模块导入

任何、全部

<bool> = any(<collection>)                                # False if empty.
<bool> = all(el[1] for el in <collection>)                # True if empty.

条件表达式

<obj> = <exp_if_true> if <condition> else <exp_if_false>

>>> [a if a else 'zero' for a in (0, 1, 2, 3)]
['zero', 1, 2, 3]

命名元组、枚举、数据类

from collections import namedtuple
Point     = namedtuple('Point', 'x y')
point     = Point(0, 0)

from enum import Enum
Direction = Enum('Direction', 'n e s w')
direction = Direction.n

from dataclasses import make_dataclass
Creature  = make_dataclass('Creature', ['loc', 'dir'])
creature  = Creature(Point(0, 0), Direction.n)

闭合

在以下情况下,我们在Python中有一个闭包:

  • 嵌套函数引用其封闭函数的值,然后
  • 封闭函数返回嵌套函数

def get_multiplier(a):
    def out(b):
        return a * b
    return out

>>> multiply_by_3 = get_multiplier(3)
>>> multiply_by_3(10)
30
  • 如果封闭函数内的多个嵌套函数引用相同的值,则共享该值
  • 动态访问函数的第一个自由变量使用'<function>.__closure__[0].cell_contents'

部分

from functools import partial
<function> = partial(<function> [, <arg_1>, <arg_2>, ...])

>>> import operator as op
>>> multiply_by_3 = partial(op.mul, 3)
>>> multiply_by_3(10)
30
  • 在需要将函数作为参数传递的情况下,Partial也很有用,因为它使我们能够预先设置其参数
  • 以下是几个例子:'defaultdict(<function>)''iter(<function>, to_exclusive)'和数据类的'field(default_factory=<function>)'

非本地

如果将变量赋给作用域中的任何位置,则将其视为局部变量,除非将其声明为“全局”或“非局部”

def get_counter():
    i = 0
    def out():
        nonlocal i
        i += 1
        return i
    return out

>>> counter = get_counter()
>>> counter(), counter(), counter()
(1, 2, 3)

装饰师

装饰者接受一个函数,添加一些功能并返回它

@decorator_name
def function_that_gets_passed_to_decorator():
    ...

调试器示例

每次调用时打印函数名称的装饰器

from functools import wraps

def debug(func):
    @wraps(func)
    def out(*args, **kwargs):
        print(func.__name__)
        return func(*args, **kwargs)
    return out

@debug
def add(x, y):
    return x + y
  • Wraps是一个辅助装饰器,它将传递的函数(Func)的元数据复制到它正在包装(Out)的函数
  • 没有它的话'add.__name__'会回来'out'

LRU缓存

缓存函数返回值的装饰器。所有函数的参数都必须是可哈希的

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n-2) + fib(n-1)
  • 默认情况下,CPython解释器将递归深度限制为1000。要增加它的使用量,请执行以下操作'sys.setrecursionlimit(<depth>)'

参数化装饰器

接受参数的修饰器,并返回接受函数的普通修饰器

from functools import wraps

def debug(print_result=False):
    def decorator(func):
        @wraps(func)
        def out(*args, **kwargs):
            result = func(*args, **kwargs)
            print(func.__name__, result if print_result else '')
            return result
        return out
    return decorator

@debug(print_result=True)
def add(x, y):
    return x + y

班级

class <name>:
    def __init__(self, a):
        self.a = a
    def __repr__(self):
        class_name = self.__class__.__name__
        return f'{class_name}({self.a!r})'
    def __str__(self):
        return str(self.a)

    @classmethod
    def get_class_name(cls):
        return cls.__name__
  • repr()的返回值应该是明确的,并且str()的返回值应该是可读的
  • 如果只定义了repr(),它也将用于str()

str()使用案例:

print(<el>)
print(f'{<el>}')
raise Exception(<el>)
loguru.logger.debug(<el>)
csv.writer(<file>).writerow([<el>])

repr()用例:

print([<el>])
print(f'{<el>!r}')
>>> <el>
loguru.logger.exception()
Z = dataclasses.make_dataclass('Z', ['a']); print(Z(<el>))

构造函数重载

class <name>:
    def __init__(self, a=None):
        self.a = a

继承

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

class Employee(Person):
    def __init__(self, name, age, staff_num):
        super().__init__(name, age)
        self.staff_num = staff_num

多重继承

class A: pass
class B: pass
class C(A, B): pass

MRO确定在搜索方法时遍历父类的顺序:

>>> C.mro()
[<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]

属性

实现getter和setter的Python方法

class MyClass:
    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value

>>> el = MyClass()
>>> el.a = 123
>>> el.a
123

数据类

自动生成init()、repr()和eq()特殊方法的装饰器

from dataclasses import dataclass, field

@dataclass(order=False, frozen=False)
class <class_name>:
    <attr_name_1>: <type>
    <attr_name_2>: <type> = <default_value>
    <attr_name_3>: list/dict/set = field(default_factory=list/dict/set)
  • 可以使用以下命令使对象可排序'order=True'并且不会随'frozen=True'
  • 要使对象可哈希,所有属性都必须是可哈希的,并且冻结必须为True
  • 函数字段()是必需的,因为'<attr_name>: list = []'将生成一个在所有实例之间共享的列表
  • DEFAULT_FACTORY可以是ANYcallable

内联:

from dataclasses import make_dataclass
<class> = make_dataclass('<class_name>', <coll_of_attribute_names>)
<class> = make_dataclass('<class_name>', <coll_of_tuples>)
<tuple> = ('<attr_name>', <type> [, <default_value>])

插槽

一种将对象限制为“插槽”中列出的属性并显著减少其内存占用的机制

class MyClassWithSlots:
    __slots__ = ['a']
    def __init__(self):
        self.a = 1

复制

from copy import copy, deepcopy

<object> = copy(<object>)
<object> = deepcopy(<object>)

鸭种类型

鸭子类型是一种隐式类型,它规定了一组特殊的方法。任何定义了这些方法的对象都被视为该鸭子类型的成员

可比的

  • 如果eq()方法未被重写,则返回'id(self) == id(other)',它与'self is other'
  • 这意味着默认情况下,所有对象的比较结果并不相等
  • 只有左侧对象调用了eq()方法,除非它返回NotImplemented,在这种情况下会咨询右侧对象

class MyComparable:
    def __init__(self, a):
        self.a = a
    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.a == other.a
        return NotImplemented

可哈希

  • 哈希对象同时需要hash()和eq()方法,并且其哈希值不应更改
  • 比较相等的Hasable对象必须具有相同的散列值,这意味着返回的默认hash()'id(self)'不会成功的
  • 这就是为什么如果只实现eq(),Python会自动使类不可散列

class MyHashable:
    def __init__(self, a):
        self._a = a
    @property
    def a(self):
        return self._a
    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.a == other.a
        return NotImplemented
    def __hash__(self):
        return hash(self.a)

可排序的

  • 使用TOTAL_ORDING修饰符,您只需要提供eq()和lt()、gt()、le()或ge()中的一个特殊方法

from functools import total_ordering

@total_ordering
class MySortable:
    def __init__(self, a):
        self.a = a
    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.a == other.a
        return NotImplemented
    def __lt__(self, other):
        if isinstance(other, type(self)):
            return self.a < other.a
        return NotImplemented

迭代器

  • 任何具有方法Next()和ITER()的对象都是迭代器
  • Next()应返回下一项或引发StopIteration
  • iter()应返回“”self“”

class Counter:
    def __init__(self):
        self.i = 0
    def __next__(self):
        self.i += 1
        return self.i
    def __iter__(self):
        return self

>>> counter = Counter()
>>> next(counter), next(counter), next(counter)
(1, 2, 3)

Python具有许多不同的迭代器对象:

可调用

  • 所有函数和类都有call()方法,因此是可调用的
  • 当此小抄使用'<function>'作为一个论点,它实际上意味着'<callable>'

class Counter:
    def __init__(self):
        self.i = 0
    def __call__(self):
        self.i += 1
        return self.i

>>> counter = Counter()
>>> counter(), counter(), counter()
(1, 2, 3)

上下文管理器

  • Enter()应锁定资源并可选地返回对象
  • exit()应该释放资源
  • 在WITH挡路中发生的任何异常都会传递给exit()方法
  • 如果它希望取消该异常,则必须返回TRUE值

class MyOpen:
    def __init__(self, filename):
        self.filename = filename
    def __enter__(self):
        self.file = open(self.filename)
        return self.file
    def __exit__(self, exc_type, exception, traceback):
        self.file.close()

>>> with open('test.txt', 'w') as file:
...     file.write('Hello World!')
>>> with MyOpen('test.txt') as file:
...     print(file.read())
Hello World!

可重复使用的鸭种

可迭代的

  • 唯一需要的方法是ITER()。它应该返回对象项的迭代器
  • CONTAINS()自动作用于定义了ITER()的任何对象

class MyIterable:
    def __init__(self, a):
        self.a = a
    def __iter__(self):
        return iter(self.a)
    def __contains__(self, el):
        return el in self.a

>>> obj = MyIterable([1, 2, 3])
>>> [el for el in obj]
[1, 2, 3]
>>> 1 in obj
True

集合

  • 唯一需要的方法是iter()和len()
  • 这张小抄实际上意味着'<iterable>'当它使用'<collection>'
  • 我选择不使用“iterable”这个名称,因为它听起来比“集合”更可怕、更模糊。

class MyCollection:
    def __init__(self, a):
        self.a = a
    def __iter__(self):
        return iter(self.a)
    def __contains__(self, el):
        return el in self.a
    def __len__(self):
        return len(self.a)

序列

  • 只有len()和getitem()是必需的方法
  • Getitem()应返回索引处的项或引发IndexError
  • ITER()和CONTAINS()自动处理定义了getitem()的任何对象
  • reverted()自动作用于定义了len()和getitem()的任何对象

class MySequence:
    def __init__(self, a):
        self.a = a
    def __iter__(self):
        return iter(self.a)
    def __contains__(self, el):
        return el in self.a
    def __len__(self):
        return len(self.a)
    def __getitem__(self, i):
        return self.a[i]
    def __reversed__(self):
        return reversed(self.a)

ABC序列

  • 这是一个比基本序列更丰富的界面
  • 扩展它将生成ITER()、CONTAINS()、REVERED()、INDEX()和COUNT()
  • 不像'abc.Iterable''abc.Collection',它不是鸭型的。这就是为什么'issubclass(MySequence, abc.Sequence)'即使MySequence定义了所有方法,也会返回false

from collections import abc

class MyAbcSequence(abc.Sequence):
    def __init__(self, a):
        self.a = a
    def __len__(self):
        return len(self.a)
    def __getitem__(self, i):
        return self.a[i]

所需和自动可用的特殊方法表:

+------------+------------+------------+------------+--------------+
|            |  Iterable  | Collection |  Sequence  | abc.Sequence |
+------------+------------+------------+------------+--------------+
| iter()     |    REQ     |    REQ     |    Yes     |     Yes      |
| contains() |    Yes     |    Yes     |    Yes     |     Yes      |
| len()      |            |    REQ     |    REQ     |     REQ      |
| getitem()  |            |            |    REQ     |     REQ      |
| reversed() |            |            |    Yes     |     Yes      |
| index()    |            |            |            |     Yes      |
| count()    |            |            |            |     Yes      |
+------------+------------+------------+------------+--------------+
  • 其他生成缺少方法的ABC有:MutableSequence、Set、MutableSet、Mapping和MutableMapping
  • 它们所需方法的名称存储在'<abc>.__abstractmethods__'

枚举

from enum import Enum, auto

class <enum_name>(Enum):
    <member_name_1> = <value_1>
    <member_name_2> = <value_2_a>, <value_2_b>
    <member_name_3> = auto()
  • 如果auto()之前没有数值,则返回1
  • 否则,它返回最后一个数值的增量

<member> = <enum>.<member_name>                 # Returns a member.
<member> = <enum>['<member_name>']              # Returns a member or raises KeyError.
<member> = <enum>(<value>)                      # Returns a member or raises ValueError.
<str>    = <member>.name                        # Returns member's name.
<obj>    = <member>.value                       # Returns member's value.

list_of_members = list(<enum>)
member_names    = [a.name for a in <enum>]
member_values   = [a.value for a in <enum>]
random_member   = random.choice(list(<enum>))

def get_next_member(member):
    members = list(member.__class__)
    index   = (members.index(member) + 1) % len(members)
    return members[index]

内联

Cutlery = Enum('Cutlery', 'fork knife spoon')
Cutlery = Enum('Cutlery', ['fork', 'knife', 'spoon'])
Cutlery = Enum('Cutlery', {'fork': 1, 'knife': 2, 'spoon': 3})

用户定义的函数不能是值,因此必须对其进行包装:

from functools import partial
LogicOp = Enum('LogicOp', {'AND': partial(lambda l, r: l and r),
                           'OR' : partial(lambda l, r: l or r)})
  • 此特定情况下的另一个解决方案是使用模块中的函数和_()和或_(operator

例外情况

基本示例

try:
    <code>
except <exception>:
    <code>

复杂示例

try:
    <code_1>
except <exception_a>:
    <code_2_a>
except <exception_b>:
    <code_2_b>
else:
    <code_2_c>
finally:
    <code_3>
  • 中的代码。'else'挡路只有在以下情况下才会被执行'try'挡路也不例外。
  • 中的代码。'finally'挡路永远会被执行

捕获异常

except <exception>:
except <exception> as <name>:
except (<exception>, [...]):
except (<exception>, [...]) as <name>:
  • 还捕获异常的子类
  • 使用'traceback.print_exc()'要将错误消息打印到标准错误,请执行以下操作
  • 使用'print(<name>)'仅打印异常的原因(其参数)

引发异常

raise <exception>
raise <exception>()
raise <exception>(<el> [, ...])

重新引发捕获的异常:

except <exception> as <name>:
    ...
    raise

异常对象

arguments = <name>.args
exc_type  = <name>.__class__
filename  = <name>.__traceback__.tb_frame.f_code.co_filename
func_name = <name>.__traceback__.tb_frame.f_code.co_name
line      = linecache.getline(filename, <name>.__traceback__.tb_lineno)
error_msg = ''.join(traceback.format_exception(exc_type, <name>, <name>.__traceback__))

内置异常

BaseException
 +-- SystemExit                   # Raised by the sys.exit() function.
 +-- KeyboardInterrupt            # Raised when the user hits the interrupt key (ctrl-c).
 +-- Exception                    # User-defined exceptions should be derived from this class.
      +-- ArithmeticError         # Base class for arithmetic errors.
      |    +-- ZeroDivisionError  # Raised when dividing by zero.
      +-- AttributeError          # Raised when an attribute is missing.
      +-- EOFError                # Raised by input() when it hits end-of-file condition.
      +-- LookupError             # Raised when a look-up on a collection fails.
      |    +-- IndexError         # Raised when a sequence index is out of range.
      |    +-- KeyError           # Raised when a dictionary key or set element is not found.
      +-- NameError               # Raised when a variable name is not found.
      +-- OSError                 # Errors such as “file not found” or “disk full” (see Open).
      |    +-- FileNotFoundError  # When a file or directory is requested but doesn't exist.
      +-- RuntimeError            # Raised by errors that don't fall into other categories.
      |    +-- RecursionError     # Raised when the maximum recursion depth is exceeded.
      +-- StopIteration           # Raised by next() when run on an empty iterator.
      +-- TypeError               # Raised when an argument is of wrong type.
      +-- ValueError              # When an argument is of right type but inappropriate value.
           +-- UnicodeError       # Raised when encoding/decoding strings to/from bytes fails.

集合及其异常:

+-----------+------------+------------+------------+
|           |    List    |    Set     |    Dict    |
+-----------+------------+------------+------------+
| getitem() | IndexError |            |  KeyError  |
| pop()     | IndexError |  KeyError  |  KeyError  |
| remove()  | ValueError |  KeyError  |            |
| index()   | ValueError |            |            |
+-----------+------------+------------+------------+

有用的内置异常:

raise TypeError('Argument is of wrong type!')
raise ValueError('Argument is of right type but inappropriate value!')
raise RuntimeError('None of above!')

用户定义的异常

class MyError(Exception):
    pass

class MyInputError(MyError):
    pass

出口

通过引发SystemExit异常退出解释器

import sys
sys.exit()                        # Exits with exit code 0 (success).
sys.exit(<el>)                    # Prints to stderr and exits with 1.
sys.exit(<int>)                   # Exits with passed exit code.

打印

print(<el_1>, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
  • 使用'file=sys.stderr'有关错误的消息,请参阅
  • 使用'flush=True'强行冲刷溪流

漂亮的印刷体

from pprint import pprint
pprint(<collection>, width=80, depth=None, compact=False, sort_dicts=True)
  • 比“深度”更深的标高将替换为“”。

输入

从用户输入或管道(如果存在)中读取行

<str> = input(prompt=None)
  • 拖尾换行符被剥离
  • 在读取输入之前,提示字符串将打印到标准输出
  • 当用户点击EOF(ctrl-d/z)或输入流耗尽时引发EOFError

命令行参数

import sys
scripts_path = sys.argv[0]
arguments    = sys.argv[1:]

参数解析器

from argparse import ArgumentParser, FileType
p = ArgumentParser(description=<str>)
p.add_argument('-<short_name>', '--<name>', action='store_true')  # Flag
p.add_argument('-<short_name>', '--<name>', type=<type>)          # Option
p.add_argument('<name>', type=<type>, nargs=1)                    # First argument
p.add_argument('<name>', type=<type>, nargs='+')                  # Remaining arguments
p.add_argument('<name>', type=<type>, nargs='*')                  # Optional arguments
args  = p.parse_args()                                            # Exits on error.
value = args.<name>
  • 使用'help=<str>'要设置参数说明,请执行以下操作
  • 使用'default=<el>'要设置默认值,请执行以下操作
  • 使用'type=FileType(<mode>)'对于文件

打开

打开文件并返回相应的文件对象

<file> = open(<path>, mode='r', encoding=None, newline=None)
  • 'encoding=None'表示使用默认编码,这取决于平台。最佳实践是使用'encoding="utf-8"'任何可能的时候
  • 'newline=None'表示所有不同的行尾组合在读取时转换为‘\n’,而在写入时所有‘\n’字符转换为系统的默认行分隔符
  • 'newline=""'表示不进行转换,但输入仍被‘\n’、‘\r’或‘\r\n’上的readline()和readline()分成块

模式

  • 'r'-读取(默认)
  • 'w'-写入(截断)
  • 'x'-如果文件已存在,则写入或失败
  • 'a'-附加
  • 'w+'-读写(截断)
  • 'r+'-从头开始读写
  • 'a+'-从末尾读写
  • 't'-文本模式(默认)
  • 'b'-二进制模式

例外情况

  • 'FileNotFoundError'可以在阅读时引发'r''r+'
  • 'FileExistsError'在使用'x'
  • 'IsADirectoryError''PermissionError'可以由任何
  • 'OSError'是所有列出的异常的父类。

文件对象

<file>.seek(0)                      # Moves to the start of the file.
<file>.seek(offset)                 # Moves 'offset' chars/bytes from the start.
<file>.seek(0, 2)                   # Moves to the end of the file.
<bin_file>.seekoffset, <anchor>)  # Anchor: 0 start, 1 current position, 2 end.

<str/bytes> = <file>.read(size=-1)  # Reads 'size' chars/bytes or until EOF.
<str/bytes> = <file>.readline()     # Returns a line or empty string/bytes on EOF.
<list>      = <file>.readlines()    # Returns a list of remaining lines.
<str/bytes> = next(<file>)          # Returns a line using buffer. Do not mix.

<file>.write(<str/bytes>)           # Writes a string or bytes object.
<file>.writelines(<collection>)     # Writes a coll. of strings or bytes objects.
<file>.flush()                      # Flushes write buffer.
  • 方法不添加或去掉尾随换行符,甚至不添加或去掉写入行()

从文件读取文本

def read_file(filename):
    with open(filename, encoding='utf-8') as file:
        return file.readlines()

将文本写入文件

def write_to_file(filename, text):
    with open(filename, 'w', encoding='utf-8') as file:
        file.write(text)

路径

from os import getcwd, path, listdir
from glob import glob

<str>  = getcwd()                   # Returns the current working directory.
<str>  = path.join(<path>, ...)     # Joins two or more pathname components.
<str>  = path.abspath(<path>)       # Returns absolute path.

<str>  = path.basename(<path>)      # Returns final component of the path.
<str>  = path.dirname(<path>)       # Returns path without the final component.
<tup.> = path.splitext(<path>)      # Splits on last period of the final component.

<list> = listdir(path='.')          # Returns filenames located at path.
<list> = glob('<pattern>')          # Returns paths matching the wildcard pattern.

<bool> = path.exists(<path>)        # Or: <Path>.exists()
<bool> = path.isfile(<path>)        # Or: <DirEntry/Path>.is_file()
<bool> = path.isdir(<path>)         # Or: <DirEntry/Path>.is_dir()

目录条目

使用scandir()代替listdir()可以显著提高同样需要文件类型信息的代码的性能

from os import scandir

<iter> = scandir(path='.')          # Returns DirEntry objects located at path.
<str>  = <DirEntry>.path            # Returns whole path as a string.
<str>  = <DirEntry>.name            # Returns final component as a string.
<file> = open(<DirEntry>)           # Opens the file and returns file object.

路径对象

from pathlib import Path

<Path> = Path(<path> [, ...])       # Accepts strings, Paths and DirEntry objects.
<Path> = <path> / <path> [/ ...]    # One of the paths must be a Path object.

<Path> = Path()                     # Returns relative cwd. Also Path('.').
<Path> = Path.cwd()                 # Returns absolute cwd. Also Path().resolve().
<Path> = Path.home()                # Returns user's home directory.
<Path> = Path(__file__).resolve()   # Returns script's path if cwd wasn't changed.

<Path> = <Path>.parent              # Returns Path without final component.
<str>  = <Path>.name                # Returns final component as a string.
<str>  = <Path>.stem                # Returns final component without extension.
<str>  = <Path>.suffix              # Returns final component's extension.
<tup.> = <Path>.parts               # Returns all components as strings.

<iter> = <Path>.iterdir()           # Returns dir contents as Path objects.
<iter> = <Path>.glob('<pattern>')   # Returns Paths matching the wildcard pattern.

<str>  = str(<Path>)                # Returns path as a string.
<file> = open(<Path>)               # Opens the file and returns file object.

操作系统命令

文件和目录

  • 路径可以是字符串、路径或DirEntry对象
  • 函数通过引发OSError或其subclasses

import os, shutil

os.chdir(<path>)                    # Changes the current working directory.
os.mkdir(<path>, mode=0o777)        # Creates a directory. Mode is in octal.
os.makedirs(<path>, mode=0o777)     # Creates all directories in the path.

shutil.copy(from, to)               # Copies the file. 'to' can exist or be a dir.
shutil.copytree(from, to)           # Copies the directory. 'to' must not exist.

os.rename(from, to)                 # Renames/moves the file or directory.
os.replace(from, to)                # Same, but overwrites 'to' if it exists.

os.remove(<path>)                   # Deletes the file.
os.rmdir(<path>)                    # Deletes the empty directory.
shutil.rmtree(<path>)               # Deletes the directory.

Shell命令

import os
<str> = os.popen('<shell_command>').read()

将‘1+1’发送到基本计算器并捕获其输出:

>>> from subprocess import run
>>> run('bc', input='1 + 1\n', capture_output=True, encoding='utf-8')
CompletedProcess(args='bc', returncode=0, stdout='2\n', stderr='')

将test.in发送到在标准模式下运行的基本计算器,并将其输出保存到test.out:

>>> from shlex import split
>>> os.popen('echo 1 + 1 > test.in')
>>> run(split('bc -s'), stdin=open('test.in'), stdout=open('test.out', 'w'))
CompletedProcess(args=['bc', '-s'], returncode=0)
>>> open('test.out').read()
'2\n'

JSON

用于存储字符串和数字集合的文本文件格式

import json
<str>    = json.dumps(<object>, ensure_ascii=True, indent=None)
<object> = json.loads(<str>)

从JSON文件读取对象

def read_json_file(filename):
    with open(filename, encoding='utf-8') as file:
        return json.load(file)

将对象写入JSON文件

def write_to_json_file(filename, an_object):
    with open(filename, 'w', encoding='utf-8') as file:
        json.dump(an_object, file, ensure_ascii=False, indent=2)

泡菜

用于存储对象的二进制文件格式

import pickle
<bytes>  = pickle.dumps(<object>)
<object> = pickle.loads(<bytes>)

从文件读取对象

def read_pickle_file(filename):
    with open(filename, 'rb') as file:
        return pickle.load(file)

将对象写入文件

def write_to_pickle_file(filename, an_object):
    with open(filename, 'wb') as file:
        pickle.dump(an_object, file)

CSV

用于存储电子表格的文本文件格式

import csv

朗读

<reader> = csv.reader(<file>)       # Also: `dialect='excel', delimiter=','`.
<list>   = next(<reader>)           # Returns next row as a list of strings.
<list>   = list(<reader>)           # Returns list of remaining rows.
  • 打开文件时必须使用'newline=""'参数,否则将无法正确解释嵌入在带引号的字段中的换行符!

<writer> = csv.writer(<file>)       # Also: `dialect='excel', delimiter=','`.
<writer>.writerow(<collection>)     # Encodes objects using `str(<el>)`.
<writer>.writerows(<coll_of_coll>)  # Appends multiple rows.
  • 打开文件时必须使用'newline=""'参数,或在使用‘\r\n’行结尾的平台上的每个‘\n’前面添加‘\r’!

参数

  • 'dialect'-设置默认值的主参数
  • 'delimiter'-用于分隔字段的单字符字符串
  • 'quotechar'-用于引用包含特殊字符的字段的字符
  • 'doublequote'-字段内的报价是加倍还是转义
  • 'skipinitialspace'-分隔符后的空格是否被剥离
  • 'lineterminator'-指定编写器如何终止行
  • 'quoting'-控制报价数量:0-根据需要,1-全部
  • 'escapechar'-如果‘doublequote’为false,则用于转义‘quotechar’的字符

方言

+------------------+--------------+--------------+--------------+
|                  |     excel    |   excel-tab  |     unix     |
+------------------+--------------+--------------+--------------+
| delimiter        |       ','    |      '\t'    |       ','    |
| quotechar        |       '"'    |       '"'    |       '"'    |
| doublequote      |      True    |      True    |      True    |
| skipinitialspace |     False    |     False    |     False    |
| lineterminator   |    '\r\n'    |    '\r\n'    |      '\n'    |
| quoting          |         0    |         0    |         1    |
| escapechar       |      None    |      None    |      None    |
+------------------+--------------+--------------+--------------+

从CSV文件读取行

def read_csv_file(filename):
    with open(filename, encoding='utf-8', newline='') as file:
        return list(csv.reader(file))

将行写入CSV文件

def write_to_csv_file(filename, rows):
    with open(filename, 'w', encoding='utf-8', newline='') as file:
        writer = csv.writer(file)
        writer.writerows(rows)

SQLite

将每个数据库存储到单独文件中的无服务器数据库引擎

连接

打开到数据库文件的连接。如果路径不存在,则创建新文件

import sqlite3
<conn> = sqlite3.connect(<path>)                # Also ':memory:'.
<conn>.close()                                  # Closes the connection.

朗读

返回值的类型可以是STR、INT、FLOAT、BYTES或NONE

<cursor> = <conn>.execute('<query>')            # Can raise a subclass of sqlite3.Error.
<tuple>  = <cursor>.fetchone()                  # Returns next row. Also next(<cursor>).
<list>   = <cursor>.fetchall()                  # Returns remaining rows. Also list(<cursor>).

<conn>.execute('<query>')                       # Can raise a subclass of sqlite3.Error.
<conn>.commit()                                 # Saves all changes since the last commit.
<conn>.rollback()                               # Discards all changes since the last commit.

或者:

with <conn>:                                    # Exits the block with commit() or rollback(),
    <conn>.execute('<query>')                   # depending on whether an exception occurred.

占位符

  • 传递的值可以是STR、INT、FLOAT、BYTES、NONE、BOOL、datetime.date或datetime.datetme类型
  • 布尔值将以整数形式存储并返回,日期形式为ISO formatted strings

<conn>.execute('<query>', <list/tuple>)         # Replaces '?'s in query with values.
<conn>.execute('<query>', <dict/namedtuple>)    # Replaces ':<key>'s with values.
<conn>.executemany('<query>', <coll_of_above>)  # Runs execute() multiple times.

示例

在此示例中,并未实际保存值,因为'conn.commit()'被省略了!

>>> conn = sqlite3.connect('test.db')
>>> conn.execute('CREATE TABLE person (person_id INTEGER PRIMARY KEY, name, height)')
>>> conn.execute('INSERT INTO person VALUES (NULL, ?, ?)', ('Jean-Luc', 187)).lastrowid
1
>>> conn.execute('SELECT * FROM person').fetchall()
[(1, 'Jean-Luc', 187)]

MySQL

具有非常相似的界面,不同之处如下所示

# $ pip3 install mysql-connector
from mysql import connector
<conn>   = connector.connect(host=<str>, …)     # `user=<str>, password=<str>, database=<str>`.
<cursor> = <conn>.cursor()                      # Only cursor has execute method.
<cursor>.execute('<query>')                     # Can raise a subclass of connector.Error.
<cursor>.execute('<query>', <list/tuple>)       # Replaces '%s's in query with values.
<cursor>.execute('<query>', <dict/namedtuple>)  # Replaces '%(<key>)s's with values.

字节数

Bytes对象是一个不变的单字节序列。可变版本称为字节数组

<bytes> = b'<str>'                       # Only accepts ASCII characters and \x00-\xff.
<int>   = <bytes>[<index>]               # Returns int in range from 0 to 255.
<bytes> = <bytes>[<slice>]               # Returns bytes even if it has only one element.
<bytes> = <bytes>.join(<coll_of_bytes>)  # Joins elements using bytes as a separator.

编码

<bytes> = bytes(<coll_of_ints>)          # Ints must be in range from 0 to 255.
<bytes> = bytes(<str>, 'utf-8')          # Or: <str>.encode('utf-8')
<bytes> = <int>.to_bytes(n_bytes, …)     # `byteorder='big/little', signed=False`.
<bytes> = bytes.fromhex('<hex>')         # Hex pairs can be separated by spaces.

解码

<list>  = list(<bytes>)                  # Returns ints in range from 0 to 255.
<str>   = str(<bytes>, 'utf-8')          # Or: <bytes>.decode('utf-8')
<int>   = int.from_bytes(<bytes>, …)     # `byteorder='big/little', signed=False`.
'<hex>' = <bytes>.hex()                  # Returns a string of hexadecimal pairs.

从文件读取字节

def read_bytes(filename):
    with open(filename, 'rb') as file:
        return file.read()

将字节写入文件

def write_bytes(filename, bytes_obj):
    with open(filename, 'wb') as file:
        file.write(bytes_obj)

结构

  • 在数字序列和字节对象之间执行转换的模块
  • 缺省情况下使用系统的类型大小和字节顺序

from struct import pack, unpack, iter_unpack

<bytes>  = pack('<format>', <num_1> [, <num_2>, ...])
<tuple>  = unpack('<format>', <bytes>)
<tuples> = iter_unpack('<format>', <bytes>)

示例

>>> pack('>hhl', 1, 2, 3)
b'\x00\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('>hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)

格式化

对于标准字体大小,格式字符串的开头为:

  • '='-系统的字节顺序(通常为小端)
  • '<'-小端字节序
  • '>'-Big-Endian(另请参阅'!')

整数类型。无符号文字使用大写字母。括号中有最小尺寸和标准尺寸:

  • 'x'-填充字节
  • 'b'-字符(1/1)
  • 'h'-短(2/2)
  • 'i'-整型(2/4)
  • 'l'-长(4/4)
  • 'q'-Long Long(8/8)

浮点类型:

  • 'f'-浮动(4/4)
  • 'd'-双倍(8/8)

阵列

只能包含预定义类型的数字的列表。上面列出了可用的类型及其以字节为单位的最小大小。大小和字节顺序始终由系统确定

from array import array
<array> = array('<typecode>', <collection>)    # Array from collection of numbers.
<array> = array('<typecode>', <bytes>)         # Array from bytes object.
<array> = array('<typecode>', <array>)         # Treats array as a sequence of numbers.
<bytes> = bytes(<array>)                       # Or: <array>.tobytes()
<file>.write(<array>)                          # Writes array to the binary file.

内存视图

  • 指向另一个对象的内存的序列对象
  • 每个元素可以引用单个或多个连续字节,具体取决于格式
  • 元素的顺序和数量可以通过切片进行更改
  • 强制转换仅在char和其他类型之间工作,并使用系统的大小和字节顺序

<mview> = memoryview(<bytes/bytearray/array>)  # Immutable if bytes, else mutable.
<real>  = <mview>[<index>]                     # Returns an int or a float.
<mview> = <mview>[<slice>]                     # Mview with rearranged elements.
<mview> = <mview>.cast('<typecode>')           # Casts memoryview to the new format.
<mview>.release()                              # Releases the object's memory buffer.

解码

<bytes> = bytes(<mview>)                       # Creates a new bytes object.
<bytes> = <bytes>.join(<coll_of_mviews>)       # Joins mviews using bytes object as sep.
<array> = array('<typecode>', <mview>)         # Treats mview as a sequence of numbers.
<file>.write(<mview>)                          # Writes mview to the binary file.

<list>  = list(<mview>)                        # Returns list of ints or floats.
<str>   = str(<mview>, 'utf-8')                # Treats mview as a bytes object.
<int>   = int.from_bytes(<mview>, …)           # `byteorder='big/little', signed=False`.
'<hex>' = <mview>.hex()                        # Treats mview as a bytes object.

Deque

一个线程安全的列表,具有高效的追加和从两端弹出的功能。发音为“deck”

from collections import deque
<deque> = deque(<collection>, maxlen=None)

<deque>.appendleft(<el>)                       # Opposite element is dropped if full.
<deque>.extendleft(<collection>)               # Collection gets reversed.
<el> = <deque>.popleft()                       # Raises IndexError if empty.
<deque>.rotate(n=1)                            # Rotates elements to the right.

穿线

  • CPython解释器一次只能运行一个线程
  • 这就是使用多个线程不会导致更快执行的原因,除非至少有一个线程包含I/O操作

from threading import Thread, RLock, Semaphore, Event, Barrier
from concurrent.futures import ThreadPoolExecutor

螺纹

<Thread> = Thread(target=<function>)           # Use `args=<collection>` to set the arguments.
<Thread>.start()                               # Starts the thread.
<bool> = <Thread>.is_alive()                   # Checks if the thread has finished executing.
<Thread>.join()                                # Waits for the thread to finish.
  • 使用'kwargs=<dict>'将关键字参数传递给函数
  • 使用'daemon=True',否则程序将无法在线程处于活动状态时退出。

锁定

<lock> = RLock()                               # Lock that can only be released by the owner.
<lock>.acquire()                               # Waits for the lock to be available.
<lock>.release()                               # Makes the lock available again.

或者:

with <lock>:                                   # Enters the block by calling acquire(),
    ...                                        # and exits it with release().

信号量,事件,屏障

<Semaphore> = Semaphore(value=1)               # Lock that can be acquired by 'value' threads.
<Event>     = Event()                          # Method wait() blocks until set() is called.
<Barrier>   = Barrier(n_times)                 # Wait() blocks until it's called n_times.

线程池执行器

管理线程执行的

<Exec> = ThreadPoolExecutor(max_workers=None)  # Or: `with ThreadPoolExecutor() as <name>: …`
<Exec>.shutdown(wait=True)                     # Blocks until all threads finish executing.

<iter> = <Exec>.map(<func>, <args_1>, ...)     # A multithreaded and non-lazy map().
<Futr> = <Exec>.submit(<func>, <arg_1>, ...)   # Starts a thread and returns its Future object.
<bool> = <Futr>.done()                         # Checks if the thread has finished executing.
<obj>  = <Futr>.result()                       # Waits for thread to finish and returns result.

队列

线程安全的FIFO队列。对于后进先出队列,请使用LifoQueue

from queue import Queue
<Queue> = Queue(maxsize=0)

<Queue>.put(<el>)                              # Blocks until queue stops being full.
<Queue>.put_nowait(<el>)                       # Raises queue.Full exception if full.
<el> = <Queue>.get()                           # Blocks until queue stops being empty.
<el> = <Queue>.get_nowait()                    # Raises queue.Empty exception if empty.

操作员

提供操作员功能的功能模块

from operator import add, sub, mul, truediv, floordiv, mod, pow, neg, abs
from operator import eq, ne, lt, le, gt, ge
from operator import and_, or_, xor, not_
from operator import itemgetter, attrgetter, methodcaller

import operator as op
elementwise_sum  = map(op.add, list_a, list_b)
sorted_by_second = sorted(<collection>, key=op.itemgetter(1))
sorted_by_both   = sorted(<collection>, key=op.itemgetter(1, 0))
product_of_elems = functools.reduce(op.mul, <collection>)
union_of_sets    = functools.reduce(op.or_, <coll_of_sets>)
LogicOp          = enum.Enum('LogicOp', {'AND': op.and_, 'OR': op.or_})
last_el          = op.methodcaller('pop')(<list>)

反省

在运行时检查代码

变量

<list> = dir()                             # Names of local variables (incl. functions).
<dict> = vars()                            # Dict of local variables. Also locals().
<dict> = globals()                         # Dict of global variables.

属性

<list> = dir(<object>)                     # Names of object's attributes (incl. methods).
<dict> = vars(<object>)                    # Dict of writable attributes. Also <obj>.__dict__.
<bool> = hasattr(<object>, '<attr_name>')  # Checks if getattr() raises an AttributeError.
value  = getattr(<object>, '<attr_name>')  # Raises AttributeError if attribute is missing.
setattr(<object>, '<attr_name>', value)    # Only works on objects with __dict__ attribute.
delattr(<object>, '<attr_name>')           # Equivalent to `del <object>.<attr_name>`.

参数

from inspect import signature
<Sig>  = signature(<function>)             # Function's Signature object.
<dict> = <Sig>.parameters                  # Dict of function's Parameter objects.
<str>  = <Param>.name                      # Parameter's name.
<memb> = <Param>.kind                      # Member of ParameterKind enum.

元编程

生成代码的代码

类型

类型是根类。如果只传递一个对象,它将返回其类型(类)。否则,它将创建一个新类

<class> = type('<class_name>', <parents_tuple>, <attributes_dict>)

>>> Z = type('Z', (), {'a': 'abcde', 'b': 12345})
>>> z = Z()

元类

创建类的类

def my_meta_class(name, parents, attrs):
    attrs['a'] = 'abcde'
    return type(name, parents, attrs)

或者:

class MyMetaClass(type):
    def __new__(cls, name, parents, attrs):
        attrs['a'] = 'abcde'
        return type.__new__(cls, name, parents, attrs)
  • new()是在init()之前调用的类方法。如果它返回其类的实例,则该实例将作为‘self’参数传递给init()
  • 它接收与init()相同的参数,只是第一个参数指定了所需的返回实例类型(在本例中为MyMetaClass)
  • 与我们的示例一样,new()也可以直接调用,通常从子类(def __new__(cls): return super().__new__(cls))
  • 以上示例之间的唯一区别是my_meta_class()返回类型为类型的类,而MyMetaClass()返回类型为MyMetaClass的类

元类属性

就在创建类之前,它检查是否定义了“metaclass”属性。如果没有,它会递归检查他的父母中是否有人定义了它,并最终进入类型()

class MyClass(metaclass=MyMetaClass):
    b = 12345

>>> MyClass.a, MyClass.b
('abcde', 12345)

类型图

type(MyClass)     == MyMetaClass     # MyClass is an instance of MyMetaClass.
type(MyMetaClass) == type            # MyMetaClass is an instance of type.

+-------------+-------------+
|   Classes   | Metaclasses |
+-------------+-------------|
|   MyClass --> MyMetaClass |
|             |     v       |
|    object -----> type <+  |
|             |     ^ +--+  |
|     str ----------+       |
+-------------+-------------+

继承图

MyClass.__base__     == object       # MyClass is a subclass of object.
MyMetaClass.__base__ == type         # MyMetaClass is a subclass of type.

+-------------+-------------+
|   Classes   | Metaclasses |
+-------------+-------------|
|   MyClass   | MyMetaClass |
|      v      |     v       |
|    object <----- type     |
|      ^      |             |
|     str     |             |
+-------------+-------------+

评估

>>> from ast import literal_eval
>>> literal_eval('[1, 2, 3]')
[1, 2, 3]
>>> literal_eval('1 + 2')
ValueError: malformed node or string

协同程序

  • 协程程序与线程有很多共同之处,但与线程不同的是,它们只在调用另一个协程程序时放弃控制,并且不使用那么多内存
  • 协程定义以'async'以及它对它的调用'await'
  • 'asyncio.run(<coroutine>)'是异步程序的主要入口点。
  • 当需要同时启动多个协程时,可以使用函数WAIT()、GATHER()和AS_COMPLETED()
  • Asyncio模块还提供了自己的QueueEventLockSemaphore班级

运行终端游戏,您可以在其中控制必须避免数字的星号:

import asyncio, collections, curses, enum, random

P = collections.namedtuple('P', 'x y')         # Position
D = enum.Enum('D', 'n e s w')                  # Direction

def main(screen):
    curses.curs_set(0)                         # Makes cursor invisible.
    screen.nodelay(True)                       # Makes getch() non-blocking.
    asyncio.run(main_coroutine(screen))        # Starts running asyncio code.

async def main_coroutine(screen):
    state = {'*': P(0, 0), **{id_: P(30, 10) for id_ in range(10)}}
    moves = asyncio.Queue()
    coros = (*(random_controller(id_, moves) for id_ in range(10)),
             human_controller(screen, moves),
             model(moves, state, *screen.getmaxyx()),
             view(state, screen))
    await asyncio.wait(coros, return_when=asyncio.FIRST_COMPLETED)

async def random_controller(id_, moves):
    while True:
        d = random.choice(list(D))
        moves.put_nowait((id_, d))
        await asyncio.sleep(random.random() / 2)

async def human_controller(screen, moves):
    while True:
        ch = screen.getch()
        key_mappings = {259: D.n, 261: D.e, 258: D.s, 260: D.w}
        if ch in key_mappings:
            moves.put_nowait(('*', key_mappings[ch]))
        await asyncio.sleep(0.01)  

async def model(moves, state, height, width):
    while state['*'] not in {p for id_, p in state.items() if id_ != '*'}:
        id_, d = await moves.get()
        p      = state[id_]
        deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
        new_p  = P(p.x + deltas[d].x, p.y + deltas[d].y)
        if 0 <= new_p.x < width-1 and 0 <= new_p.y < height:
            state[id_] = new_p

async def view(state, screen):
    while True:
        screen.clear()
        for id_, p in state.items():
            screen.addstr(p.y, p.x, str(id_))
        await asyncio.sleep(0.01)  

if __name__ == '__main__':
    curses.wrapper(main)

图书馆

进度条

# $ pip3 install tqdm
>>> from tqdm import tqdm
>>> from time import sleep
>>> for el in tqdm([1, 2, 3], desc='Processing'):
...     sleep(1)
Processing: 100%|████████████████████| 3/3 [00:03<00:00,  1.00s/it]

打印

# $ pip3 install matplotlib
import matplotlib.pyplot as plt
plt.plot(<x_data>, <y_data> [, label=<str>])   # Or: plt.plot(<y_data>)
plt.legend()                                   # Adds a legend.
plt.savefig(<path>)                            # Saves the figure.
plt.show()                                     # Displays the figure.
plt.clf()                                      # Clears the figure.

表格

将CSV文件打印为ASCII表格:

# $ pip3 install tabulate
import csv, tabulate
with open('test.csv', encoding='utf-8', newline='') as file:
    rows   = csv.reader(file)
    header = [a.title() for a in next(rows)]
    table  = tabulate.tabulate(rows, header)
    print(table)

诅咒

在终端中运行基本文件资源管理器:

from curses import wrapper, ascii, A_REVERSE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER
from os import listdir, path, chdir

def main(screen):
    ch, first, selected, paths = 0, 0, 0, listdir()
    while ch != ascii.ESC:
        height, _ = screen.getmaxyx()
        screen.clear()
        for y, a_path in enumerate(paths[first : first+height]):
            screen.addstr(y, 0, a_path, A_REVERSE * (selected == first + y))
        ch = screen.getch()
        selected += (ch == KEY_DOWN) - (ch == KEY_UP)
        selected = max(0, min(len(paths)-1, selected))
        first += (first <= selected - height) - (first > selected)
        if ch in [KEY_LEFT, KEY_RIGHT, KEY_ENTER, 10, 13]:
            new_dir = '..' if ch == KEY_LEFT else paths[selected]
            if path.isdir(new_dir):
                chdir(new_dir)
                first, selected, paths = 0, 0, listdir()

if __name__ == '__main__':
    wrapper(main)

日志记录

# $ pip3 install loguru
from loguru import logger

logger.add('debug_{time}.log', colorize=True)  # Connects a log file.
logger.add('error_{time}.log', level='ERROR')  # Another file for errors or higher.
logger.<level>('A logging message.')
  • 级别:'debug''info''success''warning''error''critical'

例外情况

自动追加异常描述、堆栈跟踪和变量值

try:
    ...
except <exception>:
    logger.exception('An error happened.')

旋转

参数,该参数设置创建新日志文件时的条件

rotation=<int>|<datetime.timedelta>|<datetime.time>|<str>
  • '<int>'-最大文件大小(字节)
  • '<timedelta>'-文件的最长使用期限
  • '<time>'-一天中的时间
  • '<str>'-以上任意字符串形式:'100 MB''1 month''monday at 12:00'

留存

设置删除旧日志文件的条件

retention=<int>|<datetime.timedelta>|<str>
  • '<int>'-最大文件数
  • '<timedelta>'-文件的最长使用期限
  • '<str>'-以字符串表示的最长时间:'1 week, 3 days''2 months'

刮刮

从Python的维基百科页面上抓取Python的URL、版本号和徽标:

# $ pip3 install requests beautifulsoup4
import requests, bs4, sys

WIKI_URL = 'https://en.wikipedia.org/wiki/Python_(programming_language)'
try:
    html       = requests.get(WIKI_URL).text
    document   = bs4.BeautifulSoup(html, 'html.parser')
    table      = document.find('table', class_='infobox vevent')
    python_url = table.find('th', text='Website').next_sibling.a['href']
    version    = table.find('th', text='Stable release').next_sibling.strings.__next__()
    logo_url   = table.find('img')['src']
    logo       = requests.get(f'https:{logo_url}').content
    with open('test.png', 'wb') as file:
        file.write(logo)
    print(python_url, version)
except requests.exceptions.ConnectionError:
    print("You've got problems with connection.", file=sys.stderr)

网络

# $ pip3 install bottle
from bottle import run, route, static_file, template, post, request, response
import json

run(host='localhost', port=8080)        # Runs locally.
run(host='0.0.0.0', port=80)            # Runs globally.

静电请求

@route('/img/<image>')
def send_image(image):
    return static_file(image, 'img_dir/', mimetype='image/png')

动态请求

@route('/<sport>')
def send_page(sport):
    return template('<h1>{{title}}</h1>', title=sport)

睡觉请求

@post('/<sport>/odds')
def odds_handler(sport):
    team = request.forms.get('team')
    home_odds, away_odds = 2.44, 3.29
    response.headers['Content-Type'] = 'application/json'
    response.headers['Cache-Control'] = 'no-cache'
    return json.dumps([team, home_odds, away_odds])

测试:

# $ pip3 install requests
>>> import threading, requests
>>> threading.Thread(target=run, daemon=True).start()
>>> url = 'http://localhost:8080/football/odds'
>>> data = {'team': 'arsenal f.c.'}
>>> response = requests.post(url, data=data)
>>> response.json()
['arsenal f.c.', 2.44, 3.29]

剖析

秒表

from time import time
start_time = time()                     # Seconds since the Epoch.
...
duration = time() - start_time

高性能:

from time import perf_counter
start_time = perf_counter()             # Seconds since the restart.
...
duration = perf_counter() - start_time

计时代码段

>>> from timeit import timeit
>>> timeit("''.join(str(i) for i in range(100))",
...        number=10000, globals=globals(), setup='pass')
0.34986

按行分析

# $ pip3 install line_profiler memory_profiler
@profile
def main():
    a = [*range(10000)]
    b = {*range(10000)}
main()

$ kernprof -lv test.py
Line #   Hits     Time  Per Hit   % Time  Line Contents
=======================================================
     1                                    @profile
     2                                    def main():
     3      1    955.0    955.0     43.7      a = [*range(10000)]
     4      1   1231.0   1231.0     56.3      b = {*range(10000)}

$ python3 -m memory_profiler test.py
Line #         Mem usage      Increment   Line Contents
=======================================================
     1        37.668 MiB     37.668 MiB   @profile
     2                                    def main():
     3        38.012 MiB      0.344 MiB       a = [*range(10000)]
     4        38.477 MiB      0.465 MiB       b = {*range(10000)}

调用图

生成突出显示瓶颈的调用图的PNG图像:

# $ pip3 install pycallgraph2
from pycallgraph2 import output, PyCallGraph
from datetime import datetime
filename = f'profile-{datetime.now():%Y%m%d%H%M%S}.png'
drawer = output.GraphvizOutput(output_file=filename)
with PyCallGraph(drawer):
    <code_to_be_profiled>

NumPy

数组操作的迷你语言。它的运行速度可以比同等的Python代码快100倍。在GPU上运行的另一种速度更快的替代方案称为CuPy

# $ pip3 install numpy
import numpy as np

<array> = np.array(<list>)
<array> = np.arange(from_inclusive, to_exclusive, ±step_size)
<array> = np.ones(<shape>)
<array> = np.random.randint(from_inclusive, to_exclusive, <shape>)

<array>.shape = <shape>
<view>  = <array>.reshape(<shape>)
<view>  = np.broadcast_to(<array>, <shape>)

<array> = <array>.sum(axis)
indexes = <array>.argmin(axis)
  • 形状是尺寸大小的元组
  • 轴是折叠的维的索引。最左边的维度具有索引0

标引

<el>       = <2d_array>[row_index, column_index]
<1d_view>  = <2d_array>[row_index]
<1d_view>  = <2d_array>[:, column_index]

<1d_array> = <2d_array>[row_indexes, column_indexes]
<2d_array> = <2d_array>[row_indexes]
<2d_array> = <2d_array>[:, column_indexes]

<2d_bools> = <2d_array> ><== <el>
<1d_array> = <2d_array>[<2d_bools>]

广播

广播是一组规则,NumPy函数根据这些规则对不同大小和/或维数的数组进行操作

left  = [[0.1], [0.6], [0.8]]        # Shape: (3, 1)
right = [ 0.1 ,  0.6 ,  0.8 ]        # Shape: (3)

1.如果数组形状的长度不同,则将较短的形状左键填入长度不同的形状:

left  = [[0.1], [0.6], [0.8]]        # Shape: (3, 1)
right = [[0.1 ,  0.6 ,  0.8]]        # Shape: (1, 3) <- !

2.如果任何维的大小不同,请通过复制其元素来展开大小为1的维:

left  = [[0.1, 0.1, 0.1], [0.6, 0.6, 0.6], [0.8, 0.8, 0.8]]  # Shape: (3, 3) <- !
right = [[0.1, 0.6, 0.8], [0.1, 0.6, 0.8], [0.1, 0.6, 0.8]]  # Shape: (3, 3) <- !

3.如果两个不匹配的维度都没有大小1,则引发错误

示例

对于每个点,返回其最近点的索引([0.1, 0.6, 0.8] => [1, 2, 1]):

>>> points = np.array([0.1, 0.6, 0.8])
 [ 0.1,  0.6,  0.8]
>>> wrapped_points = points.reshape(3, 1)
[[ 0.1],
 [ 0.6],
 [ 0.8]]
>>> distances = wrapped_points - points
[[ 0. , -0.5, -0.7],
 [ 0.5,  0. , -0.2],
 [ 0.7,  0.2,  0. ]]
>>> distances = np.abs(distances)
[[ 0. ,  0.5,  0.7],
 [ 0.5,  0. ,  0.2],
 [ 0.7,  0.2,  0. ]]
>>> i = np.arange(3)
[0, 1, 2]
>>> distances[i, i] = np.inf
[[ inf,  0.5,  0.7],
 [ 0.5,  inf,  0.2],
 [ 0.7,  0.2,  inf]]
>>> distances.argmin(1)
[1, 2, 1]

图像

# $ pip3 install pillow
from PIL import Image

<Image> = Image.new('<mode>', (width, height))  # Also: `color=<int/tuple/str>`.
<Image> = Image.open(<path>)                    # Identifies format based on file contents.
<Image> = <Image>.convert('<mode>')             # Converts image to the new mode.
<Image>.save(<path>)                            # Selects format based on the path extension.
<Image>.show()                                  # Opens image in default preview app.

<int/tuple> = <Image>.getpixel((x, y))          # Returns a pixel.
<Image>.putpixel((x, y), <int/tuple>)           # Writes a pixel to the image.
<ImagingCore> = <Image>.getdata()               # Returns a sequence of pixels.
<Image>.putdata(<list/ImagingCore>)             # Writes a sequence of pixels.
<Image>.paste(<Image>, (x, y))                  # Writes an image to the image.

<2d_array> = np.array(<Image_L>)                # Creates NumPy array from greyscale image.
<3d_array> = np.array(<Image_RGB>)              # Creates NumPy array from color image.
<Image>    = Image.fromarray(<array>)           # Creates image from NumPy array of floats.

模式

  • '1'-1位黑白像素,每个字节存储一个像素
  • 'L'-8位像素,灰度
  • 'RGB'-3×8位像素,真彩色
  • 'RGBA'-4×8位像素,真彩色,带透明蒙版
  • 'HSV'-3×8位像素、色调、饱和度、值颜色空间

示例

创建彩虹渐变的PNG图像:

WIDTH, HEIGHT = 100, 100
size = WIDTH * HEIGHT
hues = (255 * i/size for i in range(size))
img = Image.new('HSV', (WIDTH, HEIGHT))
img.putdata([(int(h), 255, 255) for h in hues])
img.convert('RGB').save('test.png')

向PNG图像添加噪波:

from random import randint
add_noise = lambda value: max(0, min(255, value + randint(-20, 20)))
img = Image.open('test.png').convert('HSV')
img.putdata([(add_noise(h), s, v) for h, s, v in img.getdata()])
img.convert('RGB').save('test.png')

图像绘制

from PIL import ImageDraw
<ImageDraw> = ImageDraw.Draw(<Image>)

<ImageDraw>.point((x, y), fill=None)
<ImageDraw>.line((x1, y1, x2, y2 [, ...]), fill=None, width=0, joint=None) 
<ImageDraw>.arc((x1, y1, x2, y2), from_deg, to_deg, fill=None, width=0)
<ImageDraw>.rectangle((x1, y1, x2, y2), fill=None, outline=None, width=0)
<ImageDraw>.polygon((x1, y1, x2, y2 [, ...]), fill=None, outline=None)
<ImageDraw>.ellipse((x1, y1, x2, y2), fill=None, outline=None, width=0)
  • 使用'fill=<color>'要设置主色,请执行以下操作
  • 使用'outline=<color>'要设置辅助颜色,请执行以下操作
  • 颜色可以指定为整数、元组'#rrggbb[aa]'字符串或颜色名称

动画

创建反弹球的GIF:

# $ pip3 install imageio
from PIL import Image, ImageDraw
import imageio
WIDTH, R = 126, 10
frames = []
for velocity in range(1, 16):
    y = sum(range(velocity))
    frame = Image.new('L', (WIDTH, WIDTH))
    draw  = ImageDraw.Draw(frame)
    draw.ellipse((WIDTH/2-R, y, WIDTH/2+R, y+R*2), fill='white')
    frames.append(frame)
frames += reversed(frames[1:-1])
imageio.mimsave('test.gif', frames, duration=0.03)

音频

import wave

<Wave_read>  = wave.open('<path>', 'rb')        # Opens the WAV file.
framerate    = <Wave_read>.getframerate()       # Number of frames per second.
nchannels    = <Wave_read>.getnchannels()       # Number of samples per frame.
sampwidth    = <Wave_read>.getsampwidth()       # Sample size in bytes.
nframes      = <Wave_read>.getnframes()         # Number of frames.
<params>     = <Wave_read>.getparams()          # Immutable collection of above.
<bytes>      = <Wave_read>.readframes(nframes)  # Returns next 'nframes' frames.

<Wave_write> = wave.open('<path>', 'wb')        # Truncates existing file.
<Wave_write>.setframerate(<int>)                # 44100 for CD, 48000 for video.
<Wave_write>.setnchannels(<int>)                # 1 for mono, 2 for stereo.
<Wave_write>.setsampwidth(<int>)                # 2 for CD quality sound.
<Wave_write>.setparams(<params>)                # Sets all parameters.
<Wave_write>.writeframes(<bytes>)               # Appends frames to the file.
  • Bytes对象包含一系列帧,每个帧由一个或多个样本组成
  • 在立体声信号中,帧的第一个样本属于左声道
  • 每个样本由一个或多个字节组成,当转换为整数时,表示扬声器膜在给定时刻的位移
  • 如果采样宽度为1,则应对整数进行无符号编码
  • 对于所有其他大小,整数应使用小端字节顺序进行有符号编码

样本值

+-----------+-------------+------+-------------+
| sampwidth |     min     | zero |     max     |
+-----------+-------------+------+-------------+
|     1     |           0 |  128 |         255 |
|     2     |      -32768 |    0 |       32767 |
|     3     |    -8388608 |    0 |     8388607 |
|     4     | -2147483648 |    0 |  2147483647 |
+-----------+-------------+------+-------------+

从WAV文件读取浮动样本

def read_wav_file(filename):
    def get_int(bytes_obj):
        an_int = int.from_bytes(bytes_obj, 'little', signed=sampwidth!=1)
        return an_int - 128 * (sampwidth == 1)
    with wave.open(filename, 'rb') as file:
        sampwidth = file.getsampwidth()
        frames = file.readframes(-1)
    bytes_samples = (frames[i : i+sampwidth] for i in range(0, len(frames), sampwidth))
    return [get_int(b) / pow(2, sampwidth * 8 - 1) for b in bytes_samples]

将浮点采样写入WAV文件

def write_to_wav_file(filename, float_samples, nchannels=1, sampwidth=2, framerate=44100):
    def get_bytes(a_float):
        a_float = max(-1, min(1 - 2e-16, a_float))
        a_float += sampwidth == 1
        a_float *= pow(2, sampwidth * 8 - 1)
        return int(a_float).to_bytes(sampwidth, 'little', signed=sampwidth!=1) 
    with wave.open(filename, 'wb') as file:
        file.setnchannels(nchannels)
        file.setsampwidth(sampwidth)
        file.setframerate(framerate)
        file.writeframes(b''.join(get_bytes(f) for f in float_samples))

示例

将正弦波保存为单声道WAV文件:

from math import pi, sin
samples_f = (sin(i * 2 * pi * 440 / 44100) for i in range(100000))
write_to_wav_file('test.wav', samples_f)

向单声道WAV文件添加噪波:

from random import random
add_noise = lambda value: value + (random() - 0.5) * 0.03
samples_f = (add_noise(f) for f in read_wav_file('test.wav'))
write_to_wav_file('test.wav', samples_f)

播放WAV文件:

# $ pip3 install simpleaudio
from simpleaudio import play_buffer
with wave.open('test.wav', 'rb') as file:
    p = file.getparams()
    frames = file.readframes(-1)
    play_buffer(frames, p.nchannels, p.sampwidth, p.framerate)

文本到语音转换

# $ pip3 install pyttsx3
import pyttsx3
engine = pyttsx3.init()
engine.say('Sally sells seashells by the seashore.')
engine.runAndWait()

合成器

格森·金斯利(Gershon Kingsley)饰演爆米花:

# $ pip3 install simpleaudio
import math, struct, simpleaudio
from itertools import repeat, chain
F  = 44100
P1 = '71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,,'
P2 = '71♩,73♪,,74♩,73♪,,74♪,,71♪,,73♩,71♪,,73♪,,69♪,,71♩,69♪,,71♪,,67♪,,71♩,,,'
get_pause   = lambda seconds: repeat(0, int(seconds * F))
sin_f       = lambda i, hz: math.sin(i * 2 * math.pi * hz / F)
get_wave    = lambda hz, seconds: (sin_f(i, hz) for i in range(int(seconds * F)))
get_hz      = lambda key: 8.176 * 2 ** (int(key) / 12)
parse_note  = lambda note: (get_hz(note[:2]), 1/4 if '♩' in note else 1/8)
get_samples = lambda note: get_wave(*parse_note(note)) if note else get_pause(1/8)
samples_f   = chain.from_iterable(get_samples(n) for n in f'{P1}{P1}{P2}'.split(','))
samples_b   = b''.join(struct.pack('<h', int(f * 30000)) for f in samples_f)
simpleaudio.play_buffer(samples_b, 1, 2, F)

PYGAME

基本示例

# $ pip3 install pygame
import pygame as pg
pg.init()
screen = pg.display.set_mode((500, 500))
rect = pg.Rect(240, 240, 20, 20)
while all(event.type != pg.QUIT for event in pg.event.get()):
    deltas = {pg.K_UP: (0, -1), pg.K_RIGHT: (1, 0), pg.K_DOWN: (0, 1), pg.K_LEFT: (-1, 0)}
    for key_code, is_pressed in enumerate(pg.key.get_pressed()):
        rect = rect.move(deltas[key_code]) if key_code in deltas and is_pressed else rect
    screen.fill((0, 0, 0))
    pg.draw.rect(screen, (255, 255, 255), rect)
    pg.display.flip()

矩形

用于存储直角坐标的对象

<Rect> = pg.Rect(x, y, width, height)           # Floats get truncated into ints.
<int>  = <Rect>.x/y/centerx/centery/# Top, right, bottom, left. Allows assignments.
<tup.> = <Rect>.topleft/center/# Topright, bottomright, bottomleft.
<Rect> = <Rect>.move((x, y))                    # Use move_ip() to move in place.

<bool> = <Rect>.collidepoint((x, y))            # Checks if rectangle contains a point.
<bool> = <Rect>.colliderect(<Rect>)             # Checks if two rectangles overlap.
<int>  = <Rect>.collidelist(<list_of_Rect>)     # Returns index of first colliding Rect or -1.
<list> = <Rect>.collidelistall(<list_of_Rect>)  # Returns indexes of all colliding Rects.

曲面

用于表示图像的对象

<Surf> = pg.display.set_mode((width, height))   # Returns display surface.
<Surf> = pg.Surface((width, height), …)         # New RGB surface. Add `pg.SRCALPHA` for RGBA.
<Surf> = pg.image.load('<path>')                # Loads the image. Format depends on source.
<Surf> = <Surf>.subsurface(<Rect>)              # Returns a subsurface.

<Surf>.fill(color)                              # Tuple, Color('#rrggbb[aa]') or Color(<name>).
<Surf>.set_at((x, y), color)                    # Updates pixel.
<Surf>.blit(<Surf>, (x, y))                     # Draws passed surface to the surface.

from pygame.transform import scale, ...
<Surf> = scale(<Surf>, (width, height))         # Returns scaled surface.
<Surf> = rotate(<Surf>, degrees)                # Returns rotated and scaled surface.
<Surf> = flip(<Surf>, x_bool, y_bool)           # Returns flipped surface.

from pygame.draw import line, ...
line(<Surf>, color, (x1, y1), (x2, y2), width)  # Draws a line to the surface.
arc(<Surf>, color, <Rect>, from_rad, to_rad)    # Also: ellipse(<Surf>, color, <Rect>)
rect(<Surf>, color, <Rect>)                     # Also: polygon(<Surf>, color, points)

字体

<Font> = pg.font.SysFont('<name>', size)        # Loads the system font or default if missing.
<Font> = pg.font.Font('<path>', size)           # Loads the TTF file. Pass None for default.
<Surf> = <Font>.render(text, antialias, color)  # Background color can be specified at the end.

声音

<Sound> = pg.mixer.Sound('<path>')              # Loads the WAV file.
<Sound>.play()                                  # Starts playing the sound.

马里奥兄弟基本示例

import collections, dataclasses, enum, io, itertools as it, pygame as pg, urllib.request
from random import randint

P = collections.namedtuple('P', 'x y')          # Position
D = enum.Enum('D', 'n e s w')                   # Direction
SIZE, MAX_SPEED = 50, P(5, 10)                  # Screen size, Speed limit

def main():
    def get_screen():
        pg.init()
        return pg.display.set_mode(2 * [SIZE*16])
    def get_images():
        url = 'https://gto76.github.io/python-cheatsheet/web/mario_bros.png'
        img = pg.image.load(io.BytesIO(urllib.request.urlopen(url).read()))
        return [img.subsurface(get_rect(x, 0)) for x in range(img.get_width() // 16)]
    def get_mario():
        Mario = dataclasses.make_dataclass('Mario', 'rect spd facing_left frame_cycle'.split())
        return Mario(get_rect(1, 1), P(0, 0), False, it.cycle(range(3)))
    def get_tiles():
        positions = [p for p in it.product(range(SIZE), repeat=2) if {*p} & {0, SIZE-1}] + \
            [(randint(1, SIZE-2), randint(2, SIZE-2)) for _ in range(SIZE**2 // 10)]
        return [get_rect(*p) for p in positions]
    def get_rect(x, y):
        return pg.Rect(x*16, y*16, 16, 16)
    run(get_screen(), get_images(), get_mario(), get_tiles())

def run(screen, images, mario, tiles):
    clock = pg.time.Clock()
    while all(event.type != pg.QUIT for event in pg.event.get()):
        keys = {pg.K_UP: D.n, pg.K_RIGHT: D.e, pg.K_DOWN: D.s, pg.K_LEFT: D.w}
        pressed = {keys.get(i) for i, on in enumerate(pg.key.get_pressed()) if on}
        update_speed(mario, tiles, pressed)
        update_position(mario, tiles)
        draw(screen, images, mario, tiles, pressed)
        clock.tick(28)

def update_speed(mario, tiles, pressed):
    x, y = mario.spd
    x += 2 * ((D.e in pressed) - (D.w in pressed))
    x -= x // abs(x) if x else 0
    y += 1 if D.s not in get_boundaries(mario.rect, tiles) else (D.n in pressed) * -10
    mario.spd = P(*[max(-limit, min(limit, s)) for limit, s in zip(MAX_SPEED, P(x, y))])

def update_position(mario, tiles):
    x, y = mario.rect.topleft
    n_steps = max(abs(s) for s in mario.spd)
    for _ in range(n_steps):
        mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect, tiles))
        x, y = x + mario.spd.x/n_steps, y + mario.spd.y/n_steps
        mario.rect.topleft = x, y

def get_boundaries(rect, tiles):
    deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
    return {d for d, delta in deltas.items() if rect.move(delta).collidelist(tiles) != -1}

def stop_on_collision(spd, bounds):
    return P(x=0 if (D.w in bounds and spd.x < 0) or (D.e in bounds and spd.x > 0) else spd.x,
             y=0 if (D.n in bounds and spd.y < 0) or (D.s in bounds and spd.y > 0) else spd.y)

def draw(screen, images, mario, tiles, pressed):
    def get_frame_index():
        if D.s not in get_boundaries(mario.rect, tiles):
            return 4
        return next(mario.frame_cycle) if {D.w, D.e} & pressed else 6
    screen.fill((85, 168, 255))
    mario.facing_left = (D.w in pressed) if {D.w, D.e} & pressed else mario.facing_left
    screen.blit(images[get_frame_index() + mario.facing_left * 9], mario.rect)
    for rect in tiles:
        screen.blit(images[18 if {*rect.topleft} & {0, (SIZE-1)*16} else 19], rect)
    pg.display.flip()

if __name__ == '__main__':
    main()

熊猫

# $ pip3 install pandas
import pandas as pd
from pandas import Series, DataFrame

系列

带名称的有序词典

>>> Series([1, 2], index=['x', 'y'], name='a')
x    1
y    2
Name: a, dtype: int64

<Sr> = Series(<list>)                         # Assigns RangeIndex starting at 0.
<Sr> = Series(<dict>)                         # Takes dictionary's keys for index.
<Sr> = Series(<dict/Series>, index=<list>)    # Only keeps items with keys specified in index.

<el> = <Sr>.loc[key]                          # Or: <Sr>.iloc[index]
<Sr> = <Sr>.loc[keys]                         # Or: <Sr>.iloc[indexes]
<Sr> = <Sr>.loc[from_key : to_key_inclusive]  # Or: <Sr>.iloc[from_i : to_i_exclusive]

<el> = <Sr>[key/index]                        # Or: <Sr>.key
<Sr> = <Sr>[keys/indexes]                     # Or: <Sr>[<key_range/range>]
<Sr> = <Sr>[bools]                            # Or: <Sr>.i/loc[bools]

<Sr> = <Sr> ><== <el/Sr>                      # Returns a Series of bools.
<Sr> = <Sr> +-*/ <el/Sr>                      # Items with non-matching keys get value NaN.

<Sr> = <Sr>.append(<Sr>)                      # Or: pd.concat(<coll_of_Sr>)
<Sr> = <Sr>.combine_first(<Sr>)               # Adds items that are not yet present.
<Sr>.update(<Sr>)                             # Updates items that are already present.

聚合、变换、映射:

<el> = <Sr>.sum/max/mean/idxmax/all()         # Or: <Sr>.aggregate(<agg_func>)
<Sr> = <Sr>.rank/diff/cumsum/ffill/interpl()  # Or: <Sr>.agg/transform(<trans_func>)
<Sr> = <Sr>.fillna(<el>)                      # Or: <Sr>.apply/agg/transform/map(<map_func>)
  • 这条路'aggregate()''transform()'首先通过向其传递单个值来确定传递的函数接受的是元素还是整个系列,如果引发错误,则将其传递给整个系列

>>> sr = Series([1, 2], index=['x', 'y'])
x    1
y    2

+-------------+-------------+-------------+---------------+
|             |    'sum'    |   ['sum']   | {'s': 'sum'}  |
+-------------+-------------+-------------+---------------+
| sr.apply(…) |      3      |    sum  3   |     s  3      |
| sr.agg(…)   |             |             |               |
+-------------+-------------+-------------+---------------+

+-------------+-------------+-------------+---------------+
|             |    'rank'   |   ['rank']  | {'r': 'rank'} |
+-------------+-------------+-------------+---------------+
| sr.apply(…) |             |      rank   |               |
| sr.agg(…)   |     x  1    |   x     1   |    r  x  1    |
| sr.trans(…) |     y  2    |   y     2   |       y  2    |
+-------------+-------------+-------------+---------------+
  • 最后一个结果有一个分层索引。使用'<Sr>[key_1, key_2]'去获取它的价值

数据帧

带有标签行和列的表

>>> DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
   x  y
a  1  2
b  3  4

<DF>    = DataFrame(<list_of_rows>)           # Rows can be either lists, dicts or series.
<DF>    = DataFrame(<dict_of_columns>)        # Columns can be either lists, dicts or series.

<el>    = <DF>.loc[row_key, column_key]       # Or: <DF>.iloc[row_index, column_index]
<Sr/DF> = <DF>.loc[row_key/s]                 # Or: <DF>.iloc[row_index/es]
<Sr/DF> = <DF>.loc[:, column_key/s]           # Or: <DF>.iloc[:, column_index/es]
<DF>    = <DF>.loc[row_bools, column_bools]   # Or: <DF>.iloc[row_bools, column_bools]

<Sr/DF> = <DF>[column_key/s]                  # Or: <DF>.column_key
<DF>    = <DF>[row_bools]                     # Keeps rows as specified by bools.
<DF>    = <DF>[<DF_of_bools>]                 # Assigns NaN to False values.

<DF>    = <DF> ><== <el/Sr/DF>                # Returns DF of bools. Sr is treated as a row.
<DF>    = <DF> +-*/ <el/Sr/DF>                # Items with non-matching keys get value NaN.

<DF>    = <DF>.set_index(column_key)          # Replaces row keys with values from a column.
<DF>    = <DF>.reset_index()                  # Moves row keys to a column named index.
<DF>    = <DF>.filter('<regex>', axis=1)      # Only keeps columns whose key matches the regex.
<DF>    = <DF>.melt(id_vars=column_key/s)     # Converts DataFrame from wide to long format.

合并、联接、合并:

>>> l = DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
   x  y 
a  1  2 
b  3  4 
>>> r = DataFrame([[4, 5], [6, 7]], index=['b', 'c'], columns=['y', 'z'])
   y  z
b  4  5
c  6  7

+------------------------+---------------+------------+------------+--------------------------+
|                        |    'outer'    |   'inner'  |   'left'   |       Description        |
+------------------------+---------------+------------+------------+--------------------------+
| l.merge(r, on='y',     |    x   y   z  | x   y   z  | x   y   z  | Joins/merges on column.  |
|            how=…)      | 0  1   2   .  | 3   4   5  | 1   2   .  | Also accepts left_on and |
|                        | 1  3   4   5  |            | 3   4   5  | right_on parameters.     |
|                        | 2  .   6   7  |            |            | Uses 'inner' by default. |
+------------------------+---------------+------------+------------+--------------------------+
| l.join(r, lsuffix='l', |    x yl yr  z |            | x yl yr  z | Joins/merges on row keys.|
|           rsuffix='r', | a  1  2  .  . | x yl yr  z | 1  2  .  . | Uses 'left' by default.  |
|           how=…)       | b  3  4  4  5 | 3  4  4  5 | 3  4  4  5 | If r is a series, it is  |
|                        | c  .  .  6  7 |            |            | treated as a column.     |
+------------------------+---------------+------------+------------+--------------------------+
| pd.concat([l, r],      |    x   y   z  |     y      |            | Adds rows at the bottom. |
|           axis=0,      | a  1   2   .  |     2      |            | Uses 'outer' by default. |
|           join=…)      | b  3   4   .  |     4      |            | A series is treated as a |
|                        | b  .   4   5  |     4      |            | column. Use l.append(r)  |
|                        | c  .   6   7  |     6      |            | to add a row instead.    |
+------------------------+---------------+------------+------------+--------------------------+
| pd.concat([l, r],      |    x  y  y  z |            |            | Adds columns at the      |
|           axis=1,      | a  1  2  .  . | x  y  y  z |            | right end. Uses 'outer'  |
|           join=…)      | b  3  4  4  5 | 3  4  4  5 |            | by default. A series is  |
|                        | c  .  .  6  7 |            |            | treated as a column.     |
+------------------------+---------------+------------+------------+--------------------------+
| l.combine_first(r)     |    x   y   z  |            |            | Adds missing rows and    |
|                        | a  1   2   .  |            |            | columns. Also updates    |
|                        | b  3   4   5  |            |            | items that contain NaN.  |
|                        | c  .   6   7  |            |            | R must be a DataFrame.   |
+------------------------+---------------+------------+------------+--------------------------+

聚合、变换、映射:

<Sr> = <DF>.sum/max/mean/idxmax/all()         # Or: <DF>.apply/agg/transform(<agg_func>)
<DF> = <DF>.rank/diff/cumsum/ffill/interpl()  # Or: <DF>.apply/agg/transform(<trans_func>)
<DF> = <DF>.fillna(<el>)                      # Or: <DF>.applymap(<map_func>)
  • 默认情况下,所有操作都在列上操作。使用'axis=1'参数来处理行,而不是处理行。

>>> df = DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y'])
   x  y
a  1  2
b  3  4

+-------------+-------------+-------------+---------------+
|             |    'sum'    |   ['sum']   | {'x': 'sum'}  |
+-------------+-------------+-------------+---------------+
| df.apply(…) |             |       x  y  |               |
| df.agg(…)   |     x  4    |  sum  4  6  |     x  4      |
|             |     y  6    |             |               |
+-------------+-------------+-------------+---------------+

+-------------+-------------+-------------+---------------+
|             |    'rank'   |   ['rank']  | {'x': 'rank'} |
+-------------+-------------+-------------+---------------+
| df.apply(…) |      x  y   |      x    y |        x      |
| df.agg(…)   |   a  1  1   |   rank rank |     a  1      |
| df.trans(…) |   b  2  2   | a    1    1 |     b  2      |
|             |             | b    2    2 |               |
+-------------+-------------+-------------+---------------+
  • 使用'<DF>[col_key_1, col_key_2][row_key]'要获取第五个结果的值,请执行以下操作

编码、解码:

<DF> = pd.read_json/html('<str/path/url>')
<DF> = pd.read_csv/pickle/excel('<path/url>')
<DF> = pd.read_sql('<table_name/query>', <connection>)
<DF> = pd.read_clipboard()

<dict> = <DF>.to_dict(['d/l/s/sp/r/i'])
<str>  = <DF>.to_json/html/csv/markdown/latex([<path>])
<DF>.to_pickle/excel(<path>)
<DF>.to_sql('<table_name>', <connection>)

分组依据

对象,该对象根据传递的列的值将数据帧的行分组在一起。

>>> df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 6]], index=list('abc'), columns=list('xyz'))
>>> df.groupby('z').get_group(3)
   x  y
a  1  2
>>> df.groupby('z').get_group(6)
   x  y
b  4  5
c  7  8

<GB> = <DF>.groupby(column_key/s)             # DF is split into groups based on passed column.
<DF> = <GB>.get_group(group_key/s)            # Selects a group by value of grouping column.

聚合、变换、映射:

<DF> = <GB>.sum/max/mean/idxmax/all()         # Or: <GB>.apply/agg(<agg_func>)
<DF> = <GB>.rank/diff/cumsum/ffill()          # Or: <GB>.aggregate(<trans_func>)  
<DF> = <GB>.fillna(<el>)                      # Or: <GB>.transform(<map_func>)

>>> gb = df.groupby('z')
      x  y  z
3: a  1  2  3
6: b  4  5  6
   c  7  8  6

+-------------+-------------+-------------+-------------+---------------+
|             |    'sum'    |    'rank'   |   ['rank']  | {'x': 'rank'} |
+-------------+-------------+-------------+-------------+---------------+
| gb.agg(…)   |      x   y  |      x  y   |      x    y |        x      |
|             |  z          |   a  1  1   |   rank rank |     a  1      |
|             |  3   1   2  |   b  1  1   | a    1    1 |     b  1      |
|             |  6  11  13  |   c  2  2   | b    1    1 |     c  2      |
|             |             |             | c    2    2 |               |
+-------------+-------------+-------------+-------------+---------------+
| gb.trans(…) |      x   y  |      x  y   |             |               |
|             |  a   1   2  |   a  1  1   |             |               |
|             |  b  11  13  |   b  1  1   |             |               |
|             |  c  11  13  |   c  1  1   |             |               |
+-------------+-------------+-------------+-------------+---------------+

轧制

用于滚动窗口计算的对象

<R_Sr/R_DF/R_GB> = <Sr/DF/GB>.rolling(window_size)  # Also: `min_periods=None, center=False`.
<R_Sr/R_DF>      = <R_DF/R_GB>[column_key/s]        # Or: <R>.column_key
<Sr/DF/DF>       = <R_Sr/R_DF/R_GB>.sum/max/mean()  # Or: <R>.apply/agg(<agg_func/str>)

插图地

# $ pip3 install plotly kaleido
from plotly.express import line
<Figure> = line(<DF>, x=<col_name>, y=<col_name>)        # Or: line(x=<list>, y=<list>)
<Figure>.update_layout(margin=dict(t=0, r=0, b=0, l=0))  # Or: paper_bgcolor='rgba(0, 0, 0, 0)'
<Figure>.write_html/json/image('<path>')                 # Also: <Figure>.show()

按大陆划分的Covid死亡人数:

Covid Deaths

covid = pd.read_csv('https://covid.ourworldindata.org/data/owid-covid-data.csv', 
                    usecols=['iso_code', 'date', 'total_deaths', 'population'])
continents = pd.read_csv('https://datahub.io/JohnSnowLabs/country-and-continent-codes-' + \
                         'list/r/country-and-continent-codes-list-csv.csv',
                         usecols=['Three_Letter_Country_Code', 'Continent_Name'])
df = pd.merge(covid, continents, left_on='iso_code', right_on='Three_Letter_Country_Code')
df = df.groupby(['Continent_Name', 'date']).sum().reset_index()
df['Total Deaths per Million'] = df.total_deaths * 1e6 / df.population
df = df[('2020-03-14' < df.date) & (df.date < '2020-11-25')]
df = df.rename({'date': 'Date', 'Continent_Name': 'Continent'}, axis='columns')
line(df, x='Date', y='Total Deaths per Million', color='Continent').show()

确认的Covid案例,道琼斯,黄金和比特币价格:

Covid Cases

import pandas as pd
import plotly.graph_objects as go

def main():
    display_data(wrangle_data(*scrape_data()))

def scrape_data():
    def scrape_covid():
        url = 'https://covid.ourworldindata.org/data/owid-covid-data.csv'
        df = pd.read_csv(url, usecols=['location', 'date', 'total_cases'])
        return df[df.location == 'World'].set_index('date').total_cases
    def scrape_yahoo(slug):
        url = f'https://query1.finance.yahoo.com/v7/finance/download/{slug}' + \
              '?period1=1579651200&period2=1608850800&interval=1d&events=history'
        df = pd.read_csv(url, usecols=['Date', 'Close'])
        return df.set_index('Date').Close
    return scrape_covid(), scrape_yahoo('BTC-USD'), scrape_yahoo('GC=F'), scrape_yahoo('^DJI')

def wrangle_data(covid, bitcoin, gold, dow):
    df = pd.concat([bitcoin, gold, dow], axis=1)
    df = df.sort_index().interpolate()
    df = df.rolling(10, min_periods=1, center=True).mean()
    df = df.loc['2020-02-23':'2020-11-25']
    df = (df / df.iloc[0]) * 100
    return pd.concat([covid, df], axis=1, join='inner')

def display_data(df):
    df.columns = ['Total Cases', 'Bitcoin', 'Gold', 'Dow Jones']
    figure = go.Figure()
    for col_name in df:
        yaxis = 'y1' if col_name == 'Total Cases' else 'y2'
        trace = go.Scatter(x=df.index, y=df[col_name], name=col_name, yaxis=yaxis)
        figure.add_trace(trace)
    figure.update_layout(
        yaxis1=dict(title='Total Cases', rangemode='tozero'),
        yaxis2=dict(title='%', rangemode='tozero', overlaying='y', side='right'),
        legend=dict(x=1.1)
    ).show()

if __name__ == '__main__':
    main()

PySimpleGUI

# $ pip3 install PySimpleGUI
import PySimpleGUI as sg
layout = [[sg.Text("What's your name?")], [sg.Input()], [sg.Button('Ok')]]
window = sg.Window('Window Title', layout)
event, values = window.read()
print(f'Hello {values[0]}!' if event == 'Ok' else '')

附录

Cython

将Python代码编译为C++的库

# $ pip3 install cython
import pyximport; pyximport.install()
import <cython_script>
<cython_script>.main()

定义:

  • 'cdef'定义是可选的,但它们有助于提高速度
  • 脚本需要使用'pyx'分机

cdef <type> <var_name> = <el>
cdef <type>[n_elements] <var_name> = [<el_1>, <el_2>, ...]
cdef <type/void> <func_name>(<type> <arg_name_1>, ...):

cdef class <class_name>:
    cdef public <type> <attr_name>
    def __init__(self, <type> <arg_name>):
        self.<attr_name> = <arg_name>

cdef enum <enum_name>: <member_name_1>, <member_name_2>, ...

PyInstaller

$ pip3 install pyinstaller
$ pyinstaller script.py                        # Compiles into './dist/script' directory.
$ pyinstaller script.py --onefile              # Compiles into './dist/script' console app.
$ pyinstaller script.py --windowed             # Compiles into './dist/script' windowed app.
$ pyinstaller script.py --add-data '<path>:.'  # Adds file to the root of the executable.
  • 文件路径需要更新为'os.path.join(sys._MEIPASS, <path>)'

基本脚本模板

#!/usr/bin/env python3
#
# Usage: .py
#

from sys import argv, exit
from collections import defaultdict, namedtuple
from dataclasses import make_dataclass
from enum import Enum
import functools as ft, itertools as it, operator as op, re


def main():
    pass


###
##  UTIL
#

def read_file(filename):
    with open(filename, encoding='utf-8') as file:
        return file.readlines()


if __name__ == '__main__':
    main()

索引

  • 仅在PDF
  • ⌘+F/CTRL F通常就足够了
  • 搜索'#<title>'在一个webpage将搜索范围限制在书目

D2l-zh 动手学深度学习

本开源项目代表了我们的一种尝试:我们将教给读者概念、背景知识和代码;我们将在同一个地方阐述剖析问题所需的批判性思维、解决问题所需的数学知识,以及实现解决方案所需的工程技能.

我们的目标是创建一个为实现以下目标的统一资源:

  1. 所有人均可在网上免费获取;
  2. 提供足够的技术深度,从而帮助读者实际成为深度学习应用科学家:既理解数学原理,又能够实现并不断改进方法;
  3. 包含可运行的代码,为读者展示如何在实际中解决问题.这样不仅直接将数学公式对应成实际代码,而且可以修改代码、观察结果并及时获取经验;
  4. 允许我们和整个社区不断快速迭代内容,从而紧跟仍在高速发展的深度学习领域;
  5. 由包含有关技术细节问答的论坛作为补充,使大家可以相互答疑并交换经验.
将本书(中英文版)用作教材或参考书的大学

如果本书对你有帮助,请星空(★)本仓库或引用本书的英文版:

@article{zhang2021dive,
    title={Dive into Deep Learning},
    author={Zhang, Aston and Lipton, Zachary C. and Li, Mu and Smola, Alexander J.},
    journal={arXiv preprint arXiv:2106.11342},
    year={2021}
}

本书的第二版

虽然纸质书第一版已经出版,但深度学习领域依然在迅速发展.为了得到来自更广泛的英文开源社区的帮助,从而提升本书质量,本书的第二版正在用英文写.英文版正不断被搬回中文版中.

目前,英文版已超过160节(中文版共96节),例如增加了理论背景(如优化收敛分析)、硬件设计(如参数服务器)、全新篇章(如注意力机制、推荐系统、深度学习的数学、生成对抗网络)、应用种类(如自然语言推理)、模型种类(如变压器、BERT)等,并优化重组了大量章节(如将自然语言处理篇章按从预训练表征、到模型设计、再到下游应用重构)。

欢迎关注本书第二版的英文开源项目

中英文教学资源

加州大学伯克利分校2019年年春学期Introduction to Deep Learning 课程教材(同时提供含教学视频地址的中文版课件).

学术界推荐

“如果你想深入学习,那就看看这本书吧!”

-韩家炜,acm院士、ieee院士,美国伊利诺伊大学香槟分校计算机系Michael Aiken主席教授

“这对机器学习文献来说是一个非常受欢迎的补充。”

–Bernhard Schölkopf,acm院士、德国国家科学院院士,德国马克斯·普朗克研究所智能系统院院长

“书中代码可谓‘所学即所用’。”

-周志华,acm院士、ieee院士、aaas院士,南京大学计算机科学与技术系主任

“这本书可以帮助深度学习实践者快速提升自己的能力”

-张潼,asa院士、ims院士,香港科技大学计算机系和数学系教授

工业界推荐

“一本优秀的深度学习教材,值得任何想了解深度学习何以引爆人工智能革命的人关注”

-黄仁勋,NVIDIA创始人兼首席执行官

“”动手学深度学习“是最适合工业界研发工程师学习的.我毫无保留地向广大的读者们强烈推荐。”

-余凯,地平线公司创始人&首席执行官

“强烈推荐这本书!我特别赞赏这种手脑一体的学习方式”

-漆远,蚂蚁金服副总裁、首席AI科学家

“”动手学深度学习“是一本很容易让学习者上瘾的书。”

–沈强,将门创投创始合伙人

贡献

感谢社区贡献者们为每一位读者改进这本开源书.

如何贡献|致谢|讨论或报告问题|其他

Open-source-mac-os-apps 适用于MacOS的令人敬畏的开源应用程序列表

MacOS上令人敬畏的开源应用程序列表。这个列表包含许多本地的和跨平台的应用程序。这个资源库的主要目标是找到免费的开源应用程序并开始贡献。请随意……contribute对于名单,任何建议都是受欢迎的!

要接收所有新的或流行的应用程序,您可以加入我们的telegram chanel

支持

嘿,朋友!帮我弄几个🍻好了!Patreon donate button

目录

应用程序

音频

  • AUHost-使用AVFoundation API托管AudioUnits v3的应用程序。swift_icon
  • Audacity-免费、开源、跨平台的音频软件c_icon
  • Aural Player详细说明:音频播放器是一款适用于MacOS平台的音频播放器应用程序。灵感来自于经典的Winamp Player for Windows,它的设计切中要害,易于使用。swift_icon
  • AutoMute-当耳机断开连接/Mac从睡眠中唤醒时,自动将声音静音。objective_c_icon
  • Background Music-背景音乐,MacOS音频实用程序:自动暂停您的音乐,设置各个应用程序的音量,并录制系统音频。cpp_icon
  • BlackHole-BLACKHOLE是一个现代的MacOS虚拟音频驱动程序,它允许应用程序以零额外延迟将音频传递给其他应用程序。c_icon
  • CAM-MacOS摄像机使用ffmpeg录制javascript_icon
  • Clementine-Clementine是一款适用于Windows、Linux和MacOS的现代音乐播放器和库管理器。cpp_icon
  • Cog详细说明:Cog是MacOS的开源音频播放器。基本布局是单面板播放列表界面,有两个可伸缩的抽屉,一个用于导航用户的音乐文件夹,另一个用于查看音频文件属性,如比特率。objective_c_icon
  • Karaoke Forever-举办令人惊叹的卡拉OK派对,每个人都可以在手机浏览器上排队歌曲。使用WebGL可视化效果播放MP3+G和MP4。javascript_icon
  • LocalRadio-LocalRadio是用于在您的Mac和移动设备上收听“软件定义广播”的软件。objective_c_icon
  • Lyricism-MacOS应用程序,向你显示当前iTunes或Spotify正在播放的歌词。objective_c_iconswift_icon
  • LyricsX-iTunes、Spotify和Vox的歌词。swift_icon
  • MPV-轻便、高度可配置的媒体播放器。c_icon
  • MonitorControl-直接从墨盒或使用键盘原生键控制外部显示器的亮度、对比度或音量。swift_iconobjective_c_icon
  • Mous Player-简单但功能强大的BSD/Linux/MacOS音频播放器。cpp_icon
  • Music Bar-音乐栏是MacOS应用程序,可将音乐控件直接放在菜单栏中。swift_icon
  • NoiseBuddy-在触摸栏或菜单栏中控制AirPods Pro上的收听模式。swift_icon
  • PlayStatus-PlayStatus是一款MacOS应用程序,允许从菜单栏控制Spotify和iTunes音乐播放。swift_icon
  • Scope-音频示波器swift_icon
  • ShazamScrobbler-可擦除的黑胶唱片、收音机、电影到Last.fm。objective_c_icon
  • Sonora-适用于MacOS的小巧、设计精美的音乐播放器。objective_c_icon
  • SpotMenu-菜单栏中的Spotify和iTunes。objective_c_iconswift_icon
  • SpotSpot-适用于MacOS的Spotify迷你播放器。javascript_icon
  • Suohai-MacOS音频输入/输出源锁。swift_icon
  • Tickeys-用于打字的即时音频反馈。MacOS版本。rust_icon
  • Tuner-乐器调谐器swift_icon
  • [Un]MuteMic-MacOS应用程序,可将麦克风的输入音量静音和取消静音。非常适合播客。objective_c_iconc_icon
  • eqMac2-适用于Mac的系统范围均衡器。cpp_icon
  • fre:ac-fre:ac音频转换器项目。cpp_icon
  • iTunes-Volume-Control-此应用程序允许您使用音量增大和音量减小热键控制iTunes音量。objective_c_icon
  • jmc-jmc是新的MacOS媒体管理器。swift_icon
  • shairport-sync-MacOS/Linux/FreeBSD/OpenBSD Airplay音频接收器。c_iconcpp_icon
  • waveSDR-使用RTL-SDR USB设备的MacOS原生桌面软件定义的无线电应用程序。swift_icon

备份

  • Mackup-使您的应用程序设置保持同步(MacOS/Linux)。python_icon
  • Syncalicious-保持多个MacOS首选项同步可能会很痛苦,但不应该如此。swift_icon
  • UrBackup-UrBackup是适用于Windows、MacOS和Linux的Client/Server网络备份。cpp_iconc_icon
  • shallow-backup-轻松创建已安装应用程序、点文件等的轻量级文档。python_icon

浏览器

  • Beaker Browser-Beaker是一款实验性的点对点Web浏览器。javascript_icon
  • Brave Browser-适用于运行Windows、MacOS和Linux的台式机和笔记本电脑的Brave浏览器。javascript_icon
  • Chromium-Chromium是一个开源浏览器项目,旨在为所有用户构建更安全、更快、更稳定的网络体验方式。javascript_iconcpp_iconc_icon
  • Fathomecat-极简设计的试验性网页浏览器。运行Windows、MacOS和Linux。javascript_icon
  • Finicky-始终打开正确的浏览器。swift_icon
  • Helium-MacOS的浮动浏览器窗口。objective_c_iconswift_icon
  • Pennywise-Pennywise在一个小的浮动窗口中打开任何网站或媒体,该窗口保留在所有其他应用程序的顶部。它是氦的一种很好的替代品。javascript_icon
  • Plash-让任何网站成为您的桌面墙纸。swift_icon
  • browserosaurus-MacOS工具,在打开链接时提示您选择浏览器。javascript_icon
  • otter-browser-水獭浏览器旨在使用Qt5重新创建经典Opera(12.x)UI的最佳方面。cpp_icon
  • seb-mac-适用于MacOS和iOS的安全检查浏览器。c_icon

聊天

  • Beagle IM-功能强大的XMPP客户端,支持文件传输、VoIP和端到端加密。swift_icon
  • ChitChat-WhatsApp Web的原生Mac应用程序包装。objective_c_icon
  • Electronic WeChat-最好是在MacOS和LINUX上运行微信。javascript_icon
  • Element-Element是一款协作应用程序(当前为Electron),适用于Matrix协议。javascript_icon
  • Franz-Franz是WhatsApp、Slake、Messenger等服务的消息应用程序。javascript_icon
  • Google Allo for Desktop-适用于Google Allo的原生MacOS和Windows桌面应用程序。javascript_icon
  • GroupMe-非官方GroupMe App。javascript_iconcss_icon
  • MessagesHistoryBrowser-MacOS应用程序,可轻松浏览和搜索Messages.app历史记录。swift_icon
  • RocketChat-团队免费开源聊天系统。Slake的替代方案,也可以自行托管。javascript_icon
  • Seaglass-一个真正的土生土长的人MatrixMacOS客户端。swift_icon
  • Signal Desktop-与您的Signal Android或Signal iOS应用程序链接的电子应用程序。javascript_icon
  • Telegram-SWIFT上MacOS的电报源代码。swift_icon
  • Telegram Desktop-Telegram桌面消息应用程序。cpp_icon
  • Textual+Text是MacOS的IRC客户端。objective_c_icon
  • Torchat-Mac-TorChat for Mac是MacOS原生的非官方torchat端口。objective_c_icon
  • WhatsAppBar-从菜单栏发送WhatsApp消息。swift_icon
  • Wire Desktop-聊天应用程序Wire的独立电子应用程序。javascript_icon

加密货币

  • Balance Open-适用于世界所有货币的App。swift_icon
  • CoinBar-MacOS菜单栏应用程序,用于跟踪加密币价格。swift_icon
  • Copay-适用于桌面和移动设备的安全比特币钱包平台。type_script_icon
  • Crypto Bar-使用Electron构建的MacOS菜单栏应用程序。javascript_icon
  • Float coin-原生菜单栏应用程序,具有浮动窗口,支持多种交换。swift_icon

数据库

  • DB Browser for SQLite详细说明:SQLite数据库管理GUI。cpp_icon
  • DBeaver-通用数据库工具和SQL客户端。java_icon
  • Medis-💻Medis是一款漂亮、易用的适用于Redis的Mac数据库管理应用程序。javascript_icon
  • MongoHub-添加另一个轻量级Mac Native MongoDB客户端。objective_c_iconc_icon
  • Postbird-MacOS的PostgreSQL GUI客户端。javascript_icon
  • Postgres.app-在Mac上开始使用PostgreSQL的最简单方式。swift_icon
  • Redis Desktop Manager-适用于Redis®的跨平台开源数据库管理工具cpp_icon
  • Redis.app-在Mac上开始使用Redis的最简单方式。swift_icon
  • Robo 3T详细说明:Robo 3T(前身为Robomongo)是面向MongoDB爱好者的免费轻量级GUI。cpp_icon
  • Sequel Ace-Sequel Ace是一款快速、易用的Mac数据库管理应用程序,用于处理MySQL和MariaDB数据库。objective_c_icon
  • Sequel Pro-MacOS的MySQL/MariaDB数据库管理。objective_c_icon
  • mongoDB.app-在Mac上开始使用MongoDB的最简单方式。swift_icon
  • reventlou-个人数据库作为信息管理系统。type_script_iconswift_icon

发展

  • Apache Netbeans-Apache NetBeans是一个IDE、工具平台和应用程序框架,适合用Java、JavaScript、PHP、HTML5、CSS等进行开发。java_icon
  • ColorSet-ColorSet是一个MacOS实用程序和框架,允许开发人员轻松管理自定义界面颜色。swift_iconc_sharp_iconobjective_c_icon
  • Layout Designer for UICollectionView-一个简单但功能强大的工具,帮助您为UICollectionView制作复杂的布局。swift_icon

Git

  • Cashew-Cashew MacOS Github问题跟踪器。objective_c_iconc_icon
  • GPM-MacOS应用程序,便于操作GitHub项目。swift_icon
  • Git Interactive Rebase Tool-基于全功能终端的序列编辑器,用于交互式重定基址。rust_icon
  • GitBlamePR-显示拉取请求的Mac应用程序上次修改了文件的每一行swift_icon
  • GitHub Desktop-从您的桌面轻松协作。type_script_icon
  • GitSync-Mac极简主义Git客户端。swift_icon
  • GitUp-你一生都怀念的Git界面终于到来了。objective_c_icon
  • GitX-GIT版本控制系统的图形客户端。objective_c_icon
  • Gitee详细说明:Github的Gitee、MacOS状态栏应用程序。objective_c_iconswift_icon
  • Github contributions-适用于iOS、WatchOS和MacOS的GitHub贡献APP。swift_icon
  • GithubListener-简单的应用程序,将通知对受监视存储库的新提交。swift_icon
  • GithubNotify-简单的MacOS应用程序,在您有未读的GitHub通知时提醒您。swift_icon
  • Gitify-菜单栏上的GitHub通知。javascript_icon
  • Streaker-GitHub贡献条纹跟踪菜单栏APP。javascript_icon
  • TeamStatus-for-GitHub-MacOS状态栏应用程序,用于跟踪团队内的代码审查过程。swift_icon
  • Trailer-管理GitHub和GitHub Enterprise的拉取请求和问题。swift_icon
  • Xit+Xit是一个处理git存储库的图形化工具。swift_icon
  • osagitfilter详细说明:过滤把开放源码语言(AppleScript,JavaScript)放入GIT中,就像它们放在纯文本文件里一样。![shell_icon]![AppleScript_icon]

JSON解析

  • JSON Mapper-简单的MacOS应用程序,用于从JSON生成SWIFT对象映射器类。swift_icon
  • JSON to Model class-基于模板的高度可定制的MacOS APP,可以从JSON字符串生成类,支持多种语言。swift_icon
  • JSONExport-适用于MacOS的桌面应用程序,使您可以将JSON对象导出为模型类,以及它们的相关构造函数、实用程序方法、setter和getter,这些都是您最喜欢的语言。swift_icon
  • j2s-MacOS应用程序,用于将JSON对象转换为SWIFT结构(当前目标是SWIFT 4和Coble)。swift_icon

其他发展

  • Boop-面向开发人员的可脚本化便签簿。swift_iconjavascript_icon
  • ChefInspector-Chef的节点和属性查看器swift_icon
  • MQTTX-优雅的跨平台MQTT 5.0桌面客户端。javascript_icontype_script_icon
  • macho-browser-MacOS Mach-O二进制文件的浏览器。objective_c_icon
  • vegvisir-基于浏览器的GUI,用于LLDB调试器。javascript_icon

Web开发

  • CoreOS VM-CoreOS VM是MacOS状态栏应用程序,它允许以一种简单的方式控制Mac上的CoreOS VM。objective_c_icon
  • Corectl App for macOS详细说明:Corectl App是一款MacOS状态栏App,其工作方式类似于corectl命令行工具corectld的包装器,用于控制服务器运行时进程。swift_icon
  • HTTP Toolkit-HTTP Toolkit是一款用于拦截、调试和模拟HTTP的跨平台工具。type_script_icon
  • Insomnia-失眠是一个跨平台的睡觉客户端,建立在电子之上。javascript_icon
  • KubeMonitor-KubeMonitor是一款MacOS应用程序,可以在菜单栏中显示有关活动的Kubernetes集群的信息。swift_icon
  • KubeSwitch-KubeSwitch在Mac的菜单栏中列出了Mac上可用的Kubernetes集群上下文。swift_icon
  • Lantern-用于网站审核和爬网的专用Mac应用程序。swift_icon
  • LocalSites-适用于MacOS的简单菜单栏(状态栏)应用程序,列出本地Bonjour网站(因为Safari 11不再有Bonjour书签)。swift_icon
  • Mockup Generator-Mockup Generator是一款MacOS应用程序,使用AngularJS/Electron构建,它位于您的菜单栏中,允许您捕获您最喜欢的网站的屏幕截图,并将其包装在设备模型中。javascript_icon
  • Now Desktop-直接从托盘菜单创建展开。javascript_icon
  • SimpleLocalize CLI-用于管理软件项目中的i18n键的开源工具。swift_icon
  • aws-s3-uploader-简单的MacOS应用程序,用于将文件上传到Amazon Web服务。javascript_icon
  • iTunesConnect-MacOS应用程序,允许您访问iTunesConnect。swift_icon
  • ndm-NPM桌面GUI。javascript_icon
  • nodeScratchpad-从菜单栏评估NodeJS/JS代码片段。swift_icon
  • stts-监控云服务状态的MacOS APP。swift_icon

iOS/MacOS

  • AVXCAssets Generator-为您的资源图像选择路径,只需单击一下即可为您创建小写字母和图像集。swift_icon
  • AppBox-iOS开发人员可通过Dropbox帐户将开发、点对点和内部(企业)应用程序直接构建和部署到设备的工具。objective_c_icon
  • AppIcons-用于生成MacOS和iOS应用程序所需的各种大小图标的工具。swift_icon
  • AppStoreReviewTimes-向您显示iOS/MacOS应用商店的平均审核时间。swift_icon
  • AppleTrace-iOS/MacOS跟踪工具。objective_c_icon
  • Asset Catalog Tinkerer-允许您打开.car文件并浏览/提取其图像的应用程序。objective_c_iconswift_icon
  • Assets-Assets是一款MacOS应用程序,用于管理您的开发项目(Xcode、Web等)的资产。swift_icon
  • Attabench-Attabench是一款适用于MacOS的微基准测试应用程序,旨在测量和可视化SWIFT代码的性能。swift_icon
  • Board For GitHub-在本机MacOS应用程序中监控您的GitHub项目网页的小型应用程序:octocat:好了!objective_c_icon
  • Brisk-用于提交雷达的MacOS应用程序。swift_icon
  • Cleaner for Xcode-Cleaner for Xcode.app,使用REACT-Native-MacOS构建。objective_c_icon
  • CocoaRestClient-用于测试HTTP/睡觉端点的原生苹果MacOS应用程序。objective_c_icon
  • Corona Tracker-适用于iOS和MacOS的冠状病毒追踪应用程序,带地图和图表。swift_icon
  • FilterShop-MacOS应用程序,用于探索CoreImage过滤器。swift_icon
  • IconGenerator-MacOS应用程序,用于生成应用程序图标。javascript_icon
  • Iconizer-在飞翔上创建XCODE镜像目录(Xcsets)。swift_icon
  • Iconology-编辑图标,然后导出到Xcode、ICNS、ICO、Favicon、MacOS图标集或自定义收藏。swift_icon
  • Icons.app-适用于MacOS的App,设计用于在各种状态、抖动(抖动)等情况下为现有应用生成大小一致的图标。objective_c_icon
  • InjectionIII-注射的快速重写已过期。objective_c_iconswift_icon
  • Knuff-Apple Push Notification Service(APNS)的调试应用程序。objective_c_icon
  • LayerX-直观的应用程序,可在屏幕上显示透明图像。swift_icon
  • Localizable.strings-Mac应用程序,用于本地化您的iOS和MacOS项目。swift_icon
  • Localization Editor-简单的MacOS编辑器应用程序,允许您并排编辑所有翻译,从而帮助您管理iOS应用程序本地化。swift_icon
  • Localizations-Localations是一款MacOS应用程序,用于管理您的Xcode项目本地化文件(.string)。swift_icon
  • Menubar Colors-MacOS应用程序,便于访问系统颜色面板。swift_icon
  • Notarize-MacOS公证状态监控工具,支持多开发者账号swift_icon
  • PodsUpdater-MacOS应用程序,帮助您管理Podfile中的依赖项释放。swift_icon
  • ProfilesManager-适用于Mac的Apple iOS/MacOS配置文件管理、.provisionprofile、.mobile配置文件管理器工具。objective_c_icon
  • PushNotifications-在iOS和Android上测试推送通知的MacOS应用。javascript_icon
  • ResignTool-这是一款适用于MacOS的应用程序,可以(重新)对应用程序进行签名,并将其捆绑到准备安装在iOS设备上的IPA文件中。objective_c_icon
  • Resizr-用于为iOS和Android应用创建AppIcon的MacOS应用。swift_icon
  • SmartPush-iOS推流通知调试App。objective_c_icon
  • TransporterPad-适用于MacOS的iOS/Android APP部署工具。swift_icon
  • WWDC-MacOS的非官方WWDC应用程序。swift_icon
  • WWDC.srt-功能强大的应用程序,可为2013年以来的每个WWDC会议视频下载(SRT)格式的字幕。swift_icon
  • Xcodes.app-安装和在多个版本的Xcode之间切换的最简单方式。swift_icon
  • calabash-launcher-iOS Calabash Launcher是一款MacOS应用程序,可帮助您在Mac上运行和管理Calabash测试。swift_icon
  • iOS Images Extractor-iOS Images Extractor是一款Mac应用程序,用于规格化、解码和提取iOS应用程序中的图像。objective_c_icon
  • iSimulator-iSimulator是一个GUI实用程序,用于控制模拟器并管理模拟器上安装的应用程序。objective_c_icon
  • xib2Storyboard-将Xcode.xib转换为.Storyboard文件的工具。objective_c_icon

下载器

  • App Downloader-轻松从巨型服务器搜索和下载MacOS应用程序homebrew cask应用程序目录。swift_icon
  • Extream Download Manager-强大的工具,可将下载速度提高高达500%java_icon
  • Get It-原生MacOS视频/音频下载器。可以把它想象成一个可以在很多网站上使用的YouTube下载器。swift_icon
  • Motrix-功能齐全的下载管理器。javascript_icon
  • Pillager用SWIFT和Objective-C编写的MacOS视频下载器。objective_c_iconswift_icon
  • YouTube Downloader for macOS-简单的菜单栏应用程序,可在Mac上下载YouTube电影。我写这篇文章是为了了解更多关于MacOS上的应用程序开发的测试项目。swift_icon
  • udemy-downloader-gui-用于下载Udemy课程的桌面应用程序。javascript_icon

编辑人员

CSV

  • TableTool详细说明:适用于MacOS的简单CSV编辑器。objective_c_icon

JSON

  • JSON Editor-使用josdejong/jsonEditor的死简单JSON编辑器![tyescript_icon]
  • JSON-Splora-用于编辑、可视化和操作JSON数据的GUI。javascript_icon

降价

  • Gingko-适用于MacOS、Windows和Linux的树形结构标记编辑器。elm_icon
  • MacDown-MacOS的Markdown编辑器。objective_c_icon
  • Mark Text-MacOS Windows和Linux的实时预览降价编辑器。javascript_icon
  • Notenik-提供多种组织选项的笔记应用程序。swift_icon
  • Pine-现代MacOS降价编辑器。swift_icon
  • QOwnNotes-纯文本文件记事本和待办事项列表管理器,支持降价和ownCloud/Nextcloud集成。cpp_icon
  • Zettlr-21世纪的降价编辑器。javascript_icontype_script_icon

特克斯

  • Qilin Editor-具有内置Katex/AsciiMath支持的精确科学文本编辑器。javascript_icon

文本

  • CotEditor-适用于MacOS的轻量级纯文本编辑器。swift_icon
  • MacVim-适用于MacOS的文本编辑器。c_icon
  • Noto-支持可定制主题的MacOS纯文本编辑器。swift_icon
  • SubEthaEdit-适用于MacOS的通用纯文本编辑器。因其实时协作功能而广为人知。objective_c_icon
  • TextMate+TextMate是MacOS的图形化文本编辑器。objective_c_icon
  • VimR-为MacOS提供精致的Neovim体验。swift_icon
  • reventlou-个人数据库作为信息管理系统。type_script_iconswift_icon

扩展部分

  • BetterPiP-在HTML5视频的Google Chrome等浏览器中使用原生画中画。swift_icon
  • Is It Private?-提供工具栏图标的Safari扩展,如果启用了隐私浏览,该图标可以更改其视觉外观。swift_icon
  • Middleclick-在MacBook触摸板和魔术鼠标上用三个手指单击或轻击来模拟滚轮单击c_icon
  • PageExtender-使用您自己的CSS和JS文件扩展页面。swift_iconjavascript_icon
  • PiPTool-在MacOS的YouTube、Netflix、Plex等视频广播服务中增加画中画功能。javascript_icon
  • PiPifier+Pifier是一个原生的MacOS 10.12 Safari扩展,允许您在Picture模式下使用Picture中的每个HTML5视频。swift_icon
  • Sessions-Safari扩展可保存您的工作会话swift_icon
  • Swimat-Swimat是一个Xcode插件,用于格式化您的SWIFT代码。swift_icon
  • ThenGenerator-‘THEN’的Xcode源代码编辑器扩展swift_icon
  • Ultra TabSaver-Ultra TabSaver是Safari的开源选项卡管理器swift_icon
  • nef-此Xcode扩展允许您选择代码并将其导出为代码片段。在Mac AppStore上提供。swift_icon

查找器

  • Clipy-Clipy是MacOS的剪贴板扩展应用程序。swift_icon
  • CopyQ-具有高级功能的剪贴板管理器cpp_icon
  • Duplicate Finder-这是一个有用的工具,可以帮助您查找特定文件夹中具有相同名称的所有重复文件。swift_icon
  • FiScript-在Finder中从MacOS上下文菜单(按住CTRL并单击)执行自定义脚本。swift_icon
  • Finder Go-MacOS应用程序和Finder Sync扩展,用于从Finder打开终端、iTerm、Hyper。swift_icon
  • OpenInCode-Finder工具栏应用程序,用于在Visual Studio代码中打开当前文件夹。objective_c_icon
  • OpenInTerminal-用于MacOS的Finder Toolbar应用程序,用于在终端、iTerm、Hyper或Alacritty中打开当前目录。swift_icon
  • Quick Look plugins-面向开发人员的实用Quick Look插件列表。objective_c_iconc_icon
  • cd to… -Finder Toolbar APP打开终端中的当前目录objective_c_icon

游戏

  • Battle for Wesnoth-基于回合的战术战略游戏,以单人和在线多人战斗为特色。cpp_icon
  • Boxer-适合您的Mac的DOS游戏模拟器。cpp_iconobjective_c_icon
  • Dolphin-功能强大的任天堂GameCube和Wii游戏模拟器。cpp_icon
  • OpenEmu-MacOS的复古视频游戏仿真。objective_c_icon
  • OpenRCT2-重新实施过山车大亨2。cpp_icon
  • Screentendo-把你的屏幕变成马里奥的可玩级别。objective_c_icon
  • Stockfish-漂亮、功能强大的国际象棋应用程序。cpp_iconobjective_c_icon

图形

  • Aseprite+动画化的精灵编辑器和像素艺术工具(Windows、MacOS、Linux)。cpp_iconc_icon
  • CaptuocrToy-通过在线OCR API截图和识别文本的工具。swift_icon
  • ColorSet-ColorSet是一个MacOS实用程序和框架,允许开发人员轻松管理自定义界面颜色。swift_iconc_sharp_iconobjective_c_icon
  • Gaphor详细说明:Gaphor是UML和SysML的简单建模工具。python_icon
  • GifCapture-适用于MacOS的Gif Capture应用程序。swift_icon
  • Gifcurry-视频到GIF Maker,具有能够裁剪、添加文本、查找和裁剪的图形界面。haskell_icon
  • Gifski-将视频转换为高质量的GIF格式。swift_icon
  • InfiniteCanvas-概念验证Mac绘图应用程序。swift_icon
  • Material Colors Native-选择您的材料颜色,并复制祸不单行代码。objective_c_icon
  • Pencil2D Animation+Pencil2D是一款适用于MacOS、Windows和Linux的动画/绘图软件。它允许您使用位图和矢量图形创建传统的手绘动画(卡通)。cpp_icon
  • ScreenToLayers for macOS+ScreenToLayers是一款MacOS应用程序,可以轻松地将屏幕捕获为分层的PSD文件。objective_c_iconcss_icon
  • macSVG详细说明:MacOS应用程序,用于设计带有WebKit Web视图的HTML5 SVG(可伸缩矢量图形)图片和动画。objective_c_icon

IDE

  • Apache Netbeans-Apache NetBeans是一个IDE、工具平台和应用程序框架,适合用Java、JavaScript、PHP、HTML5、CSS等进行开发。java_icon
  • Atom-可破解的文本编辑器。javascript_icon
  • LiveCode-跨平台开发IDE。c_icon
  • Oni-ONI是一款现代的模态编辑代码编辑器,专注于开发人员的工作效率。javascript_icontype_script_icon
  • Vim-无处不在的文本编辑器C_icon![Vim脚本图标]
  • Visual Studio Code-微软开发的代码编辑器。type_script_icon
  • ZeroBraneStudio-ZeroBrane Studio是一个轻量级的跨平台Lua IDE,具有代码补全、语法突出显示、远程调试器、代码分析器、实时编码和对各种Lua引擎的调试支持。lua_icon

图像

  • APNGb-MacOS应用程序,用于组装和拆卸动画PNG文件。swift_icon
  • Crunch详细说明:疯狂的(很慢但非常好)的PNG图像优化。python_icon
  • ExifCleaner-拖放、多核批处理、暗模式移除图像元数据。javascript_icon
  • Freehand-MacOS状态栏应用程序,用于快速绘制。swift_icon
  • Gimp详细说明:GIMP是GNU图像处理程序。c_icon
  • Iconology-编辑图标,然后导出到Xcode、ICNS、ICO、Favicon、MacOS图标集或自定义收藏。swift_icon
  • ImageAlpha-适用于pngquant、pngnq和Afterizer的Mac GUI。objective_c_iconpython_icon
  • Imagine+Imagine是一款压缩PNG和JPEG的桌面应用程序,拥有现代友好的UI。type_script_icon
  • InVesalius-3D医学成像重建软件python_icon
  • Katana-Katana是一个简单的MacOS屏幕截图实用程序,位于您的菜单栏中。javascript_iconcss_icon
  • PhotoMiner-MacOS应用程序,用于在您的磁盘上查找和丢失被遗忘的照片。swift_icon
  • Screenbar-MacOS菜单栏应用程序,用于自动截屏。swift_icon
  • Seashore详细说明:简单易用的macos图片编辑应用,适合我们的睡觉。objective_c_icon
  • WebPonize详细说明:WebPonize是一款MacOS应用程序,用于将PNG、JPEG、动画(或非动画)GIF图像转换为WebP。swift_iconc_icon

键盘

  • AnnePro-mac-MacOS应用程序,用于通过蓝牙控制AnnePro键盘。swift_icon
  • Fluor-MacOS的便捷工具,允许您根据活动的应用程序切换Fn键的模式。swift_icon
  • GokuRakuJoudo-Karabiner-Elements配置经理,拯救到臃肿的Karabine.jsonclojure_icon
  • Karabiner详细说明:Karabiner(KeyRemap4MacBook)是一款功能强大的键盘定制工具。cpp_iconobjective_c_icon
  • Karabiner-Elements-Karabiner-Elements是在MacOS Sierra(10.12)或更高版本上进行键盘自定义的强大实用程序。cpp_iconobjective_c_icon
  • Kawa-更好的MacOS输入源切换器。swift_icon
  • Thor-尽快切换正确的应用程序。swift_icon
  • Unshaky–一款软件尝试解决苹果蝴蝶键盘上的“双键按下”问题swift_icon

邮件

  • Correo-适用于Windows和MacOS的Menubar/Taskbar Gmail App。javascript_icon
  • ElectronMail-ProtonMail和Tutanota端到端加密电子邮件提供商的非官方桌面应用程序。type_script_icon
  • Mailspring-💌由原作者之一制作的漂亮、快速且维护良好的@nylas Mail叉子javascript_icon
  • Rambox-跨平台消息传递和电子邮件应用程序,将常见的Web应用程序合并为一个应用程序。javascript_iconcss_icon
  • SimpleLogin-电子邮件别名解决方案:保护您的真实电子邮件地址。swift_icon
  • dejalu-快速、简单的电子邮件客户端。cpp_iconobjective_c_icon

医疗

菜单栏

  • Airpass-状态栏Mac应用程序,可克服时间限制的WiFi网络。javascript_icon
  • AnyBar-MacOS菜单栏状态指示器。objective_c_icon
  • CloudyTabs-简单的菜单栏MacOS应用程序,用于显示iCloud选项卡和阅读列表的列表。swift_icon
  • DatWeatherDoe详细说明:简单的菜单栏天气应用程序,适用于MacOS,用SWIFT编写。swift_icon
  • DisplayMenu-简单(基本)MacOS菜单栏额外应用显示预设。swift_icon
  • Dozer-隐藏MacOS菜单栏项目。swift_icon
  • Grayscale Mode-从菜单栏管理灰度模式。swift_icon
  • Hidden Bar-帮助隐藏菜单栏图标的超轻MacOS实用程序swift_icon
  • Itsycal-Mac菜单栏中有一个小小的日历。objective_c_icon
  • KubeContext-在Mac上导入、管理和切换Kubernetes上下文。swift_icon
  • Market Bar-菜单栏的微型股票观察器。swift_icon
  • MeetingBar-日历会议的菜单栏应用程序swift_icon
  • MenuMeters-适用于MacOS的CPU、内存、磁盘和网络监控工具。objective_c_icon
  • Menubar Brightness-MacOS应用程序可更改菜单栏上的屏幕亮度。javascript_icon
  • Music Bar-音乐栏是MacOS应用程序,可将音乐控件直接放在菜单栏中。swift_icon
  • Night Shift Control-夜班控制是一个简单的MacOS菜单栏应用程序,用于控制夜班。它的目标是引入f.lux中夜班没有的功能,比如对某些应用程序禁用夜班。swift_icon
  • Nocturnal-菜单栏应用程序,具有比暗调光更暗的颜色,Night Shift微调,以及在MacBook Pro上关闭TouchBar的能力。swift_icon
  • NoiseBuddy-在触摸栏或菜单栏中控制AirPods Pro上的收听模式。swift_icon
  • PSIBar-快速黑进PSI MacOS状态栏APP。swift_icon
  • Pi Stats-MacOS应用程序,用于可视化PI-孔信息。swift_iconobjective_c_icon
  • PlayStatus-PlayStatus是一款MacOS应用程序,允许从菜单栏控制Spotify和iTunes音乐播放。swift_icon
  • Quickeys-一款Mac菜单栏应用程序,通过快速下拉菜单提供笔记功能。swift_icon
  • SensibleSideButtons-小型菜单栏实用程序,允许您使用第三方鼠标的侧键在各种应用程序之间导航。objective_c_iconc_icon
  • Shifty-MacOS菜单栏应用程序,可让您更好地控制夜班。swift_icon
  • SlimHUD – Cyanocitta-更换MacOS的音量、亮度和键盘背光HUD。swift_icon
  • Stats-菜单栏中的MacOS系统显示器swift_icon
  • SwiftBar-功能强大的MacOS菜单栏定制工具。swift_icon
  • baRSS – Menu Bar RSS Reader-位于系统状态栏中的RSS&Atom源阅读器。objective_c_icon
  • gSwitch-MacOS状态栏应用程序,允许控制双GPU MacBook上的GPU。swift_icon
  • iGlance-状态栏的MacOS系统监视器(CPU、内存、网络、风扇和电池)。swift_icon
  • xbar-将任何脚本或程序的输出放入MacOS菜单栏。objective_c_icon

音乐

  • Carol-一款留在MacOS菜单栏中的极简而漂亮的歌词应用程序。c_sharp_icon
  • ChordDetector-收听iTunes和Spotify以检测歌曲和弦的微型菜单栏应用程序!swift_icon
  • DeezPlayer-适用于Windows、Linux和MacOS的Deezer桌面应用程序。coffee_script_icon
  • Karaoke Forever-举办令人惊叹的卡拉OK派对,每个人都可以在手机浏览器上排队歌曲。使用WebGL可视化效果播放MP3+G和MP4。javascript_icon
  • Lilypond UI-用lilypond创作优美的乐谱。javascript_icon
  • MPV-轻便、高度可配置的媒体播放器。c_icon
  • Music Bar-音乐栏是MacOS应用程序,可将音乐控件直接放在菜单栏中。swift_icon
  • NoiseBuddy-在触摸栏或菜单栏中控制AirPods Pro上的收听模式。swift_icon
  • PlayStatus-PlayStatus是一款MacOS应用程序,允许从菜单栏控制Spotify和iTunes音乐播放。swift_icon
  • SoundCleod-适用于MacOS和Windows的SoundCloud。javascript_icon
  • Spotify-Cli-Mac-无需离开终端即可控制Spotify。🎶javascript_icon
  • YouTube-Music-music.youtube.com的MacOS包装器。swift_icon
  • iTunes Graphs-MacOS应用程序,将您的iTunes库可视化为图形。swift_icon
  • lyricsify-SWIFT中适用于MacOS的简单Spotify歌词查看器菜单栏应用程序。swift_icon

新闻

注意事项

  • Boostnote-为像您一样的程序员制作的笔记应用程序。javascript_icon
  • Dnote-具有多设备同步和Web界面的简单命令行笔记本。go_icontype_script_icon
  • FSNotes-MacOS/iOS的笔记管理器:类固醇的现代记号速度(NvALT)。swift_icon
  • FromScratch-可以用作快速笔记或待办事项的小应用程序。javascript_iconcss_icon
  • Jupyter Notebook Viewer-MacOS笔记本查看器。swift_icon
  • NoteTaker-适用于MacOS和iOS的简单笔记APP,使用Realm和CloudKit进行同步。swift_icon
  • Notenik-提供多种组织选项的笔记应用程序。swift_icon
  • Notes-Notes是一款MacOS应用程序,用于创建笔记,使用文本和其他格式:图像、视频、联系人等。swift_icon
  • QOwnNotes-纯文本文件记事本和待办事项列表管理器,支持降价和ownCloud/Nextcloud集成。cpp_icon
  • Simplenote-最简单的记录方式。objective_c_icon
  • Standard Notes-安全的地方存放您的笔记、思想和毕生的工作。javascript_iconcss_icon
  • Tusk-非官方的、第三方的、社区驱动的Evernote应用程序,具有一些有用的功能。javascript_iconcss_icon
  • joplin-支持Windows、MacOS、Linux、Android和iOS同步的笔记和待办应用。javascript_icon
  • notable-简单的笔记应用程序。javascript_icon
  • tmpNote详细说明:非常简单的MacOS应用程序,可以做临时笔记。swift_icon

其他

  • Cakebrew-使用Cakebrew管理您的自制配方。objective_c_icon
  • DevDocs for macOS-非官方DevDocs API文档查看器。swift_icon
  • Gas Mask-MacOS的主机文件管理器。objective_c_icon
  • Hosts-用于/etc/hosts的Cocoa GUI。objective_c_icon
  • ImageOptim-适用于Mac的GUI图像优化器。objective_c_icon
  • Keyframes Player-简单的MacOS应用程序,用于预览使用Facebook的关键帧框架创建的动画。swift_icon
  • Lepton-代码片段管理民主化(MacOS/Win/Linux)。javascript_icon
  • Letters-教你的孩子字母表和如何打字。swift_icon
  • Platypus-Mac开发工具,用于从命令行脚本创建应用程序包。objective_c_icon
  • QorumLogs-适用于Xcode和Google Docs的SWIFT日志记录实用程序。swift_icon
  • React Native Debugger-用于检查您的Reaction本地项目的桌面应用程序。MacOS、Linux和Windows。javascript_icon
  • Reactotron-桌面应用程序,用于检查您的Reaction JS和Reaction Native项目。MacOS、Linux和Windows。javascript_icon
  • RktMachine-菜单栏MacOS应用程序,用于在MacOS虚拟机管理程序CoreOS VM中运行RKT。swift_icon
  • Ruby.app-MacOS应用程序,包含完整的Ruby安装(用于Ruby/Gosu)。ruby_icon
  • Shuttle+MacOS的简单SSH快捷菜单。objective_c_icon
  • SwiftyBeaver-在SWIFT中开发和发布期间方便的日志记录。swift_icon
  • Unused-用于检查Xcode项目中未使用的资源的Mac应用程序。objective_c_icon
  • Vagrant Manager-使用适用于MacOS的流浪管理器,在一个位置管理您的流浪汉机器。objective_c_icon
  • macGist-简单的应用程序,可以将粘贴板项目发送到GitHub的Gist。swift_icon
  • syncthing-macosx-经济实惠的nativemacOS MacOS同步应用程序捆绑包。objective_c_icon

玩家

  • IINA-MacOS的现代视频播放器。swift_icon
  • MPV-轻便、高度可配置的媒体播放器。c_icon
  • MPlayerX-MacOS上的媒体播放器。objective_c_icon
  • MacMorpheus-支持MacOS for PSVR的3D 180/360视频播放器,支持头部跟踪。objective_c_icon
  • Movie Monad-使用GStreamer和GTK+的Haskell构建的桌面视频播放器。haskell_icon

播客

  • Cumulonimbus-简单、漂亮的播客应用。javascript_icon
  • Doughnut-适用于Mac的Podcast播放器和库swift_icon
  • PodcastMenu-Podcast Menu是一款简单的应用程序,它将Overcast在你的Mac的菜单栏上,这样你就可以在工作的同时收听你最喜欢的播客。swift_icon
  • Podlive for macOS-MacOS客户端收听直播播客(仅限)。它目前支持所有通过超声波播放的直播流,Studio Link On Airobjective_c_icon
  • mkchromecast-将MacOS和Linux音频/视频播放到您的Google Cast和Sonos设备。python_icon

生产力

  • Ao-优雅的微软待办事项桌面应用。javascript_iconcss_icon
  • Calculeta详细说明:适用于MacOS的计算器,适用于状态栏。swift_icon
  • Cerebro-跨平台启动器APP。javascript_icon
  • ClipMenu-MacOS的剪贴板管理器。objective_c_icon
  • Clocker-MacOS应用程序可跨时区进行规划和组织。objective_c_icon
  • ControlPlane-根据您所在的位置或所做的工作自动执行任务。objective_c_icon
  • DevUtils.app-适用于MacOS的开发实用程序,只需单击一下即可帮助您处理日常小任务!即JSON格式化程序、UUID生成器。swift_icon
  • Flycut-面向开发人员的简洁简洁的剪贴板管理器。objective_c_icon
  • Itsycal-Mac菜单栏中有一个小小的日历。objective_c_icon
  • KeyHolder-在MacOS中记录快捷方式,如Alfred.app。swift_icon
  • Kiwix-适用于iOS和MacOS的Kiwix,基于SWIFT构建。swift_icon
  • Layout Designer for UICollectionView-一个简单但功能强大的工具,帮助您为UICollectionView制作复杂的布局。swift_icon
  • Linked Ideas-MacOS应用程序,用于记录和连接想法。swift_icon
  • Mac Screenshot Tracker-开源、免费、可破解的截图跟踪器。再看一遍你一直在做的东西!python_icon
  • Maccy-轻量级的边键入边搜索剪贴板管理器。swift_icon
  • Manta-灵活的开票桌面应用程序,具有美观且可定制的模板。javascript_icon
  • MeetingBar-日历会议的菜单栏应用程序swift_icon
  • Middleclick-在MacBook触摸板和魔术鼠标上用三个手指单击或轻击来模拟滚轮单击c_icon
  • PDF Archiver-标记和归档任务的好工具。swift_icon
  • Paperless Desktop-使用无纸化API管理您的文档扫描的桌面应用程序。javascript_icon
  • Pennywise-Pennywise在一个小的浮动窗口中打开任何网站或媒体,该窗口保留在所有其他应用程序的顶部。它是氦的一种很好的替代品。javascript_icon
  • Pomodoro Cycle-适用于MacOS的Pomodoro循环type_script_icon
  • QOwnNotes-纯文本文件记事本和待办事项列表管理器,支持降价和ownCloud/Nextcloud集成。cpp_icon
  • Quicksilver-QuickSilver是一款快速的MacOS生产力应用程序,让您能够快速、优雅地控制Mac。objective_c_icon
  • Quickwords-在几秒钟内写下任何东西。创建可以使用替身文本、执行繁琐任务等功能的代码片段。javascript_iconcss_icon
  • SelfControl-MacOS应用到挡路,你自己可以在一段预定的时间内访问令人分心的网站等。这不能通过应用程序或重新启动来撤销-你必须等待计时器超时。objective_c_icon
  • Sessions-Safari扩展可保存您的工作会话swift_icon
  • Speed Reader-借助SpeedReader的静音功能,阅读速度更快。swift_icon
  • Spotter-生产力工具,主要功能是搜索和启动外部应用程序操作和应用程序本身,以便您可以专注于当前任务。有点像聚光灯或者阿尔弗雷德。type_script_iconswift_icon
  • StickyNotes-在AppKit中实现的Windows 10风格的Sticky Notes应用程序。swift_icon
  • Strategr-无需大惊小怪的时间管理。cpp_iconobjective_c_icon
  • Super Productivity-与Jira集成,为程序员和设计师提供免费的待办事项列表和时间跟踪器。type_script_iconjavascript_icon
  • ThenGenerator-‘THEN’的Xcode源代码编辑器扩展swift_icon
  • Thyme-OS X的任务计时器。objective_c_icon
  • Timer-适用于Mac的简单计时器应用程序。swift_icon
  • Toggl Desktop-适用于Windows、Mac和Linux的Toggl桌面应用程序。cpp_icon
  • TomatoBar-用于MacOS的Pomodoro技术计时器,带触摸栏支持。swift_icon
  • TrelloApp-用SWIFT编写的Trello.com非官方包装申请。这几乎是站点特定浏览器的“Hello World”。swift_icon
  • Ultra TabSaver-Ultra TabSaver是Safari的开源选项卡管理器swift_icon
  • Watson-用于时间跟踪的CLI应用程序。python_icon
  • Whale-非官方的Trello应用程序。javascript_icon
  • Yomu-MacOS版的Manga阅读器应用程序。swift_icon
  • espanso-跨平台文本扩展器,Alfred代码段的强大替代品rust_icon
  • macOrganizer-MacOS应用程序,用于组织文件或删除不必要的文件。swift_icon
  • reventlou-个人数据库作为信息管理系统。type_script_iconswift_icon
  • status-bar-todo-简单的MacOS应用程序,可将待办事项列表保存在状态栏中。swift_icon
  • stretchly-跨平台电子应用程序,提醒您在使用计算机时休息。javascript_icon

屏幕保护程序

  • Aerial-适用于MacOS的Apple TV空中屏幕保护程序。swift_icon
  • Brooklyn–2018年10月30日,受苹果活动启发的屏保。swift_icon
  • Image-As-Wallpaper-实用程序应用程序可帮助选择用作Mac电脑桌面墙纸或屏幕保护程序的图像。swift_icon
  • Irvue-MacOS的屏幕保护程序。objective_c_icon
  • Life Saver-一个基于Conway的生活游戏的抽象屏幕保护程序,用SpriteKit实现swift_icon
  • MinimalClock-简单美观的屏幕保护程序,显示时间。swift_icon
  • MusaicFM-用于Spotify和Last.fm的iTunes屏幕保护程序克隆objective_c_icon
  • Predator-MacOS的捕食者灵感时钟屏幕保护程序swift_icon
  • The GitHub Matrix Screensaver-适用于MacOS的GitHub矩阵屏幕保护程序。javascript_icon

安全性

  • Cloaker-简单的拖放、基于密码的文件加密。rust_icon
  • Cryptomator-云中文件的多平台透明客户端加密。java_icon
  • LuLu详细说明:Lulu是MacOS防火墙应用程序,旨在阻止挡路未经授权(传出)的网络流量。objective_c_icon
  • Swifty-免费和离线密码管理器。javascript_icon
  • macOS GateKeeper Helper-简单的MacOS网守脚本。它可以帮助你控制你的看门人。![shell_icon]
  • stronghold-从终端轻松配置MacOS安全设置。python_icon

共享文件

  • Deluge-轻量级跨平台BitTorrent客户端。python_icon
  • NitroShare-将文件从一台设备传输到另一台设备cpp_icon
  • Rhea-MacOS状态栏应用程序,用于快速共享文件和URL。objective_c_icon
  • Transmission-官方传输BitTorrent客户端库。objective_c_iconc_icon
  • Tribler-具有P2P内容发现功能的隐私增强型BitTorrent客户端。python_icon
  • mac2imgur-简单的Mac应用程序,旨在快速轻松地将图像和屏幕截图上传到Imgur。swift_icon
  • qBittorrent-Qt中的BitTorrent客户端。cpp_icon

社交网络

  • Caprine-优雅的Facebook Messenger桌面应用程序。javascript_iconcss_icon
  • Goofy-非官方Facebook Messenger客户端。javascript_icon
  • Leviathan详细说明:Leviathan是Mastodon社交网络的iOS和MacOS客户端应用程序。swift_icon
  • Messenger-MacOS应用程序包装了Facebook的Messenger桌面版。objective_c_icon
  • Product Hunt-分享和发现您最喜欢的新产品和应用。swift_icon
  • Quail-非官方的esa应用程序。javascript_icon
  • Ramme-非官方Instagram桌面应用。javascript_iconcss_icon
  • RedditOS-适用于MacOS的SwiftUI Reddit客户端。swift_icon
  • Simpo-MacOS菜单栏应用程序可快速发布状态。swift_icon

流式传输

  • Galeri-Perpetual Artwork流媒体应用程序。javascript_icon
  • OBS Studio-用于直播和屏幕录制的免费开源软件。cpp_icon

系统

  • AppPolice-适用于MacOS的应用程序,具有简约的UI,可让您快速降低任何正在运行的进程的CPU使用率。objective_c_icon
  • Apple Juice-适用于MacOS的高级电池电量计。swift_icon
  • Clean-Me-充当系统清洁器(日志、缓存等)的小型MacOS应用程序。swift_icon
  • Diagnostics-Diagnostics是显示MacOS上应用程序的诊断报告的应用程序。swift_icon
  • DisableMonitor-轻松禁用或启用Mac上的显示器。objective_c_icon
  • EtreCheck-EtreCheck是一款易于使用的MacOS应用程序,可显示系统配置的重要详细信息,并允许您将该信息复制到剪贴板。objective_c_icon
  • Fanny-从通知中心监控Mac的风扇速度和CPU温度。objective_c_icon
  • HoRNDIS-适用于MacOS的Android USB Tethering驱动程序。cpp_icon
  • Juice-让您的电池信息更有趣一些。swift_icon
  • KeepingYouAwake-防止您的Mac进入睡眠状态。objective_c_icon
  • Latest-适用于MacOS的小型实用程序,确保您了解您使用的应用程序的所有最新更新。swift_icon
  • Loading-适用于MacOS的简单网络活动监控器。objective_c_icon
  • Overkill-连接iPhone时停止打开iTunes。swift_icon
  • ProfileCreator-用于创建标准或自定义配置文件的MacOS应用程序。objective_c_icon
  • SlimHUD – Cyanocitta-更换MacOS的音量、亮度和键盘背光HUD。swift_icon
  • Sloth-Sloth是一个MacOS应用程序,它显示系统上所有正在运行的应用程序正在使用的所有打开的文件和套接字的列表。objective_c_icon
  • Spotter-生产力工具,主要功能是搜索和启动外部应用程序操作和应用程序本身,以便您可以专注于当前任务。有点像聚光灯或者阿尔弗雷德。type_script_iconswift_icon
  • Stats-菜单栏中的MacOS系统显示器swift_icon
  • Turbo Boost Switcher-Turbo Boost Switcher是一个适用于Mac计算机的小应用程序,允许启用和/或禁用Turbo Boost功能。objective_c_icon
  • VerticalBar-MacOS应用程序向Dock添加竖条。swift_icon
  • macOS GateKeeper Helper-简单的MacOS网守脚本。它可以帮助你控制你的看门人。![shell_icon]
  • macOSLucidaGrande-一个将Lucida Grande设置为Mac系统UI字体的小工具。objective_c_icon

终端

  • Alacritty-跨平台、GPU加速的终端仿真器。rust_icon
  • Bifrost详细说明:一个用于串口通信的小型终端仿真器(MacOS/Linux)。go_icon
  • Console-MacOS控制台应用程序。swift_icon
  • Finder Go-MacOS应用程序和Finder Sync扩展,用于从Finder打开终端、iTerm、Hyper。swift_icon
  • Hyper-基于Web技术构建的终端。javascript_iconcss_icon
  • Kitty-跨平台,快速,功能齐全,基于GPU的终端仿真器。python_iconc_icon
  • OpenInTerminal-用于MacOS的Finder Toolbar应用程序,用于在终端、iTerm、Hyper或Alacritty中打开当前目录。swift_icon
  • OpenTerminal-MacOS应用程序,打开一个新的Finder窗口,并将当前目录更改为应用程序启动的文件夹。swift_icon
  • cd to… -Finder Toolbar APP打开终端中的当前目录objective_c_icon
  • iTerm 2详细说明:MacOS终端仿真器,可以做出令人惊叹的事情。objective_c_icon
  • macOS GateKeeper Helper-简单的MacOS网守脚本。它可以帮助你控制你的看门人。![shell_icon]
  • wallpapper详细说明:WallPapper是一个控制台应用程序,用于为Mojave创建动态壁纸。swift_icon

触摸栏

  • Muse-支持TouchBar的Spotify控制器。swift_icon
  • MyTouchbarMyRules-可根据您的需要自定义触摸栏的应用程序。swift_icon
  • Pock-在触摸栏中显示MacOS坞站。swift_icon
  • Touch Bar Preview-在新MacBook Pro的触摸栏上显示您的设计的小型应用程序。swift_icon
  • Touch Bar Simulator-在任何Mac上使用触摸栏。swift_icon
  • Touch Emoji-MacBook Pro触摸栏的Emoji拾取器。swift_icon

公用事业

  • Android tool for Mac-一键截图、视频录制、iOS和Android应用安装swift_icon
  • ArchiveMounter-挂载归档文件,如磁盘映像。swift_icon
  • BeardedSpice-使用Mac键盘上的媒体密钥控制基于Web的媒体播放器。objective_c_icon
  • Bitwarden-面向个人、团队和业务组织的跨平台密码管理解决方案。type_script_icon
  • Bitwarden Menu-菜单栏中的Bitwarden密码管理器type_script_iconswift_icon
  • Boop-面向开发人员的可脚本化便签簿。swift_iconjavascript_icon
  • Buttercup Desktop-适用于Mac和其他平台的安全密码管理器。javascript_icon
  • Calculeta详细说明:适用于MacOS的计算器,适用于状态栏。swift_icon
  • Catch-捕捉:广播式捕捉变得很容易。swift_icon
  • Clear Clipboard Text Format-使用清晰的剪贴板文本格式轻松清除剪贴板文本的格式。objective_c_icon
  • CoreLocationCLI-获取设备的物理位置并将其打印到标准输出swift_icon
  • CornerCal-适用于MacOS的简单、干净的日历和时钟应用程序。swift_icon
  • Crypter-Crypter是一款创新、方便和安全的跨平台密码应用程序,通过只需记住一位MasterPass,简化了安全密码的生成和管理。javascript_icon
  • DevUtils.app-适用于MacOS的开发实用程序,只需单击一下即可帮助您处理日常小任务!即JSON格式化程序、UUID生成器。swift_icon
  • Duplicate Finder-这是一个有用的工具,可以帮助您查找特定文件夹中具有相同名称的所有重复文件。swift_icon
  • ECheck-用于验证MacOS的epub文件的小工具。swift_icon
  • Flying Carpet-通过ad-hoc wifi进行跨平台文件传输,与AirDrop类似,但适用于Mac/Windows/Linux。go_icon
  • Funky-根据每个应用程序轻松切换Mac上的功能键。objective_c_icon
  • Gray-只需单击按钮,即可在每个应用程序的浅色外观和深色外观之间进行选择swift_icon
  • Grayscale Mode-从菜单栏管理灰度模式。swift_icon
  • Kap-使用Web技术构建的屏幕记录器应用程序。javascript_icon
  • KeePassXC-Windows应用程序“Keepass Password Safe”的跨平台社区驱动端口cpp_icon
  • KeeWeb-与KeePass兼容的跨平台密码管理器。javascript_icon
  • Kyapchar-适用于MacOS的简单屏幕和麦克风录音机。swift_icon
  • Layout Designer for UICollectionView-一个简单但功能强大的工具,帮助您为UICollectionView制作复杂的布局。swift_icon
  • Life-Calendar-生活日历。swift_icon
  • Lunar-为您的外部显示器提供智能自适应亮度。swift_icon
  • MQTTX-优雅的跨平台MQTT 5.0桌面客户端。javascript_icontype_script_icon
  • MacPass-本地MacOS KeePass客户端。objective_c_icon
  • Maria-用于aria2下载工具的MacOS原生app/widget。swift_icon
  • MeetingBar-日历会议的菜单栏应用程序swift_icon
  • Meme Maker-Meme Maker MacOS应用程序,用于创建Meme。swift_icon
  • Middleclick-在MacBook触摸板和魔术鼠标上用三个手指单击或轻击来模拟滚轮单击c_icon
  • MonitorControl-直接从墨盒或使用键盘原生键控制外部显示器的亮度、对比度或音量。swift_iconobjective_c_icon
  • Monolingual-从MacOS中删除不必要的语言资源swift_icon
  • Mos-平滑鼠标滚动并反转鼠标滚动方向swift_icon
  • Music Bar-音乐栏是MacOS应用程序,可将音乐控件直接放在菜单栏中。swift_icon
  • Nocturnal-菜单栏应用程序,具有比暗调光更暗的颜色,Night Shift微调,以及在MacBook Pro上关闭TouchBar的能力。swift_icon
  • NoiseBuddy-在触摸栏或菜单栏中控制AirPods Pro上的收听模式。swift_icon
  • Noti-在您的Mac上接收Android通知(使用PushBullet)。swift_icon
  • Numi-一个具有自然语言解析功能的便捷计算器。javascript_icon
  • PB for Desktop-在MacOS、Windows和Linux上接收原生推送通知。javascript_icon
  • Padlock-MacOS的最小开源密码管理器。javascript_icon
  • PercentCalculator详细说明:一个计算参数的菜单栏应用程序。swift_icon
  • Plain Pasta-将您的剪贴板简洁化swift_icon
  • PlayStatus-PlayStatus是一款MacOS应用程序,允许从菜单栏控制Spotify和iTunes音乐播放。swift_icon
  • PowerShell-PowerShell是一个跨平台的自动化和配置工具/框架,可以与您现有的工具很好地配合使用。c_sharp_icon
  • ScreenCat-ScreenCat是一款屏幕分享+远程协作应用。javascript_iconcss_icon
  • SlimHUD – Cyanocitta-更换MacOS的音量、亮度和键盘背光HUD。swift_icon
  • SlowQuitApps-向Command-Q添加全局延迟,以停止意外应用退出。objective_c_icon
  • Spotter-生产力工具,主要功能是搜索和启动外部应用程序操作和应用程序本身,以便您可以专注于当前任务。有点像聚光灯或者阿尔弗雷德。type_script_iconswift_icon
  • Stats-菜单栏中的MacOS系统显示器swift_icon
  • Super Productivity-与Jira集成,为程序员和设计师提供免费的待办事项列表和时间跟踪器。type_script_iconjavascript_icon
  • Telephone-适用于MacOS的SIP软电话。objective_c_iconswift_icon
  • The Blockstack Browser-BlockStack是一个分散应用程序的互联网,用户拥有自己的数据。数据块堆栈浏览器允许您浏览数据块堆栈Internet。javascript_icon
  • ThenGenerator-‘THEN’的Xcode源代码编辑器扩展swift_icon
  • ToTheTop-小型MacOS应用程序,可帮助您滚动到顶部。swift_icon
  • Ultra TabSaver-Ultra TabSaver是Safari的开源选项卡管理器swift_icon
  • baRSS – Menu Bar RSS Reader-位于系统状态栏中的RSS&Atom源阅读器。objective_c_icon
  • calibre-跨平台电子书管理器。python_icon
  • fselect-使用SQL语法搜索文件的命令行工具。rust_icon
  • homebrew-cask-用于管理以二进制文件形式分发的MacOS应用程序的CLI工作流ruby_icon
  • iOScanX-用于半自动iOS APP分析和评估的Cocoa应用。objective_c_iconc_icon
  • mac-sound-fix-Mac Sound Re-Enabler。swift_icon
  • macOS GateKeeper Helper-简单的MacOS网守脚本。它可以帮助你控制你的看门人。![shell_icon]
  • wechsel-使用键盘管理蓝牙连接。swift_icon
  • Übersicht-密切关注您的机器和世界上正在发生的事情。objective_c_icon

VPN和代理

  • ShadowsocksX-NG-下一代ShadowsocksX。swift_icon
  • Specht-使用MacOS网络扩展构建的基于规则的代理应用。swift_icon
  • SpechtLite-MacOS基于规则的代理APP。swift_icon
  • Tunnelblick+Tunnelblick是MacOS上OpenVPN的图形用户界面。objective_c_icon
  • clashX-基于规则的自定义代理,带有基于Clash的Mac GUI。swift_icon
  • rvc-mac-Ribose VPN Client MacOS菜单App。swift_icon

视频

  • Acid.Cam.v2.OSX-用于MacOS的acid Cam v2会扭曲视频以创建艺术。cpp_icon
  • AppleEvents-MacOS的非官方Apple Events应用程序。objective_c_icon
  • Conferences.digital-在您的Mac上免费观看您喜爱的开发者会议上最新最棒的视频的最佳方式。swift_icon
  • Datamosh-Datamosh您在MacOS上的视频。swift_icon
  • Face Data-MacOS应用程序,用于自动注释视频中的地标。swift_icon
  • GNU Gatekeeper-用于H.323终端的视频会议服务器。cpp_icon
  • Gifted-快速轻松地将任何短视频转换为动画GIF。objective_c_icon
  • HandBrake-HANDBRAK是一款支持Linux、Mac和Windows的视频转码器。c_icon
  • MPV-轻便、高度可配置的媒体播放器。c_icon
  • MenuTube-将YouTube捕捉到您的MacOS菜单栏中!javascript_icon
  • OpenShot-易用、易学、功能强大的视频编辑器。python_icon
  • Quick Caption-无需手动输入时间代码即可转录和生成字幕文件(SRT、ASS和FCPXML)。swift_icon
  • QuickLook Video+这个软件包允许MacOS Finder显示缩略图,静电快速浏览预览,封面艺术和大多数类型视频文件的元数据。objective_c_icon
  • Subler-Subler是一款MacOS应用程序,用于多路复用和标记MP4文件。objective_c_icon
  • VLC-VLC是一个免费开源的跨平台多媒体播放器c_icon
  • Vid Quiz Creator-MacOS应用程序,用于在视频播放中插入测验,并使用LISNR API向接收设备播放这些视频。swift_icon
  • WebTorrent Desktop-流媒体激流APP。适用于Mac、Windows和Linux。javascript_icon
  • Yoda-漂亮的MacOS应用程序,可让您从YouTube浏览和下载视频。javascript_icon

壁纸

  • 500-mac-wallpaper-状态栏的简单MacOS应用程序,可自动将照片从500px.com下载到可设置为壁纸源的本地文件夹。swift_icon
  • ArtWall-ARtStATION从设置为壁纸artwork.rssobjective_c_icon
  • Artify-MacOS应用程序,致力于将18世纪的艺术带给每个人swift_icon
  • BingPaper-在MacOS上使用必应日常照片作为墙纸。swift_icon
  • Desktop Wallpaper Switcher-用于管理和循环桌面壁纸的Win/Linux/MacOS工具。cpp_icon
  • Muzei-MacOS版Muzei墙纸APP。swift_icon
  • Plash-让任何网站成为您的桌面墙纸。swift_icon
  • Satellite Eyes-MacOS应用程序可自动将您的桌面墙纸设置为上方的卫星视图。objective_c_icon
  • Sunscreen-防晒是一款有趣的轻量级应用程序,可以根据日出和日落改变您的桌面墙纸。swift_icon
  • WallpaperMenu详细说明:MacOS菜单栏应用程序,可在Web上浏览美丽的图片,并将其设置为您的桌面图像。ruby_icon
  • pyDailyChanger-pyDailyChanger是一个每天更换墙纸的程序。python_icon

窗口管理

  • Amethyst-MacOS的自动平铺窗口管理器。swift_icon
  • AppGrid-MacOS的基于网格的键盘窗口管理器。objective_c_icon
  • Desktop Profiles-适用于MacOS的创新型桌面/窗口管理器swift_icon
  • Hammerspoon-借助Lua实现功能惊人的MacOS台式机自动化。lua_iconobjective_c_icon
  • Phoenix-可使用JavaScript编写脚本的轻量级MacOS窗口和应用程序管理器。objective_c_icon
  • Rectangle详细说明:Rectangle是一个在很大程度上基于壮观的窗口管理器,用Swift编写。swift_icon
  • ShiftIt-管理窗口大小和位置。objective_c_icon
  • Slate-Slate是类似于Divvy和SizeUp的窗口管理应用程序objective_c_icon
  • Spectacle-SPOGLE允许您在不使用鼠标的情况下组织您的窗口。objective_c_icon
  • Yabai详细说明:基于二进制空间分区的MacOS平铺窗口管理器。c_iconobjective_c_icon

贡献者

感谢所有做出贡献的人:

Wtfpython – Python中的各种坑与违反直觉的奇怪问题

翻译:Chinese 中文|Vietnamese Tiếng Việt|Spanish Español|Add translation

其他模式:Interactive|CLI

Python是一种设计精美的高级和基于解释器的编程语言,它为程序员提供了许多舒适的特性。但有时,Python代码片段的结果乍一看可能并不明显

下面是一个有趣的项目,它试图解释Python中一些违反直觉的代码片段和鲜为人知的功能背后到底发生了什么

虽然您在下面看到的一些示例可能不是真正意义上的WTF,但它们将揭示您可能不知道的Python的一些有趣部分。我发现这是学习编程语言内部的一个很好的方法,我相信您也会发现它很有趣!

如果您是一名经验丰富的Python程序员,那么您可以将其视为一次尝试就能正确完成大部分操作的挑战。你以前可能已经经历过其中的一些,我也许能唤起你甜蜜的旧回忆!😅

PS:如果你是回头客,你可以了解到新的修改here(标有星号的例子为最新一次主要修订中增加的例子)

所以,我们开始吧

目录

示例的结构

所有示例的结构如下:

▶一些花哨的标题

# Set up the code.
# Preparation for the magic...

输出(Python版本):

>>> triggering_statement
Some unexpected output

(可选):一行描述意外输出

💡说明:

  • 简要说明正在发生的事情以及发生的原因

# Set up code
# More examples for further clarification (if necessary)

输出(Python版本):

>>> trigger # some example that makes it easy to unveil the magic
# some justified output

注:所有示例都在Python 3.5.2交互式解释器上进行了测试,除非在输出之前明确指定,否则它们应该适用于所有Python版本

用法

在我看来,最大限度地利用这些例子的一个很好的方法是按时间顺序阅读它们,并针对每个例子:

  • 仔细阅读设置示例的初始代码。如果您是一名经验丰富的Python程序员,您将在大多数情况下成功预测接下来会发生什么
  • 阅读输出片段,
    • 检查输出是否与您预期的相同
    • 如果您知道输出背后的确切原因,请确保它是这样的
      • 如果答案是否定的(这完全没问题),深呼吸,然后阅读解释(如果你仍然不明白,就大声喊出来!)然后制造一个问题here)
      • 如果是,轻轻拍一下你的背,你就可以跳到下一个例子了

PS:您也可以在命令行中使用pypi package

$ pip install wtfpython -U
$ wtfpython

👀示例

部分:开动脑筋!

▶当务之急!*

由于某些原因,Python3.8的“Walrus”运算符(:=)已经变得相当流行了。我们去看看吧,

1个

# Python version 3.8+

>>> a = "wtf_walrus"
>>> a
'wtf_walrus'

>>> a := "wtf_walrus"
File "<stdin>", line 1
    a := "wtf_walrus"
      ^
SyntaxError: invalid syntax

>>> (a := "wtf_walrus") # This works though
'wtf_walrus'
>>> a
'wtf_walrus'

2个

# Python version 3.8+

>>> a = 6, 9
>>> a
(6, 9)

>>> (a := 6, 9)
(6, 9)
>>> a
6

>>> a, b = 6, 9 # Typical unpacking
>>> a, b
(6, 9)
>>> (a, b = 16, 19) # Oops
  File "<stdin>", line 1
    (a, b = 16, 19)
          ^
SyntaxError: invalid syntax

>>> (a, b := 16, 19) # This prints out a weird 3-tuple
(6, 16, 19)

>>> a # a is still unchanged?
6

>>> b
16

💡解释

海象操作员快速刷新器

海象操作员(:=)是在Python3.8中引入的,因此在需要为表达式中的变量赋值的情况下会很有用

def some_func():
        # Assume some expensive computation here
        # time.sleep(1000)
        return 5

# So instead of,
if some_func():
        print(some_func()) # Which is bad practice since computation is happening twice

# or
a = some_func()
if a:
    print(a)

# Now you can concisely write
if a := some_func():
        print(a)

输出(>3.8):

5
5
5

这节省了一行代码,并且隐式地阻止了调用some_func两次

  • 不带括号的“赋值表达式”(使用walrus运算符)在顶层受到限制,因此SyntaxErrora := "wtf_walrus"第一个代码段的语句。加上括号后,它按预期工作并分配给a
  • 与往常一样,包含以下内容的表达式的括号=不允许使用操作员。因此,中的语法错误(a, b = 6, 9)
  • Walrus运算符的语法为NAME:= expr,在哪里NAME是有效的标识符,并且expr是有效的表达式。因此,不支持迭代打包和解包,这意味着,
    • (a := 6, 9)相当于((a := 6), 9)最终(a, 9) (其中a的值为6‘)

      >>> (a := 6, 9) == ((a := 6), 9)
      True
      >>> x = (a := 696, 9)
      >>> x
      (696, 9)
      >>> x[0] is a # Both reference same memory location
      True
    • 同样,(a, b := 16, 19)相当于(a, (b := 16), 19)它只不过是一个3元组

▶字符串有时可能很棘手

1个

>>> a = "some_string"
>>> id(a)
140420665652016
>>> id("some" + "_" + "string") # Notice that both the ids are same.
140420665652016

2个

>>> a = "wtf"
>>> b = "wtf"
>>> a is b
True

>>> a = "wtf!"
>>> b = "wtf!"
>>> a is b
False

3个

>>> a, b = "wtf!", "wtf!"
>>> a is b # All versions except 3.7.x
True

>>> a = "wtf!"; b = "wtf!"
>>> a is b # This will print True or False depending on where you're invoking it (python shell / ipython / as a script)
False

# This time in file some_file.py
a = "wtf!"
b = "wtf!"
print(a is b)

# prints True when the module is invoked!

4.

输出(<Python3.7)

>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False

很有道理,对吧?

💡说明:

  • 第一个和第二个代码段中的行为是由于CPython优化(称为字符串插入),在某些情况下会尝试使用现有的不可变对象,而不是每次都创建新对象
  • 在“内嵌”之后,许多变量可能会在内存中引用相同的字符串对象(从而节省内存)
  • 在上面的代码片断中,字符串是隐式驻留的。何时隐式内嵌字符串的决定取决于实现。有一些规则可用于猜测字符串是否会被扣留:
    • 所有长度为0和长度1的字符串都会被插入
    • 字符串在编译时驻留('wtf'会被拘留,但是''.join(['w', 't', 'f'])不会被拘留)
    • 不包含由ASCII字母、数字或下划线组成的字符串。这就解释了为什么'wtf!'没有被拘留是因为!可以找到此规则的CPython实现here
      image
  • 什么时候ab设置为"wtf!"在同一行中,Python解释器创建一个新对象,然后同时引用第二个变量。如果您在单独的行上执行,它不会“知道”已经有"wtf!"作为对象(因为"wtf!"根据上述事实,未被默示拘留)。它是编译时优化。此优化不适用于CPython的3.7.x版本(选中此选项issue有关更多讨论,请参见)
  • 在像IPython这样的交互式环境中,编译单元由单个语句组成,而如果是模块,则由整个模块组成。a, b = "wtf!", "wtf!"是单个语句,而a = "wtf!"; b = "wtf!"是一行中的两个语句。这就解释了为什么a = "wtf!"; b = "wtf!",并解释为什么它们在中调用时是相同的some_file.py
  • 第四个代码段的输出突然更改是由于peephole optimization一种称为恒定折叠的技术。这意味着表达式'a'*20被替换为'aaaaaaaaaaaaaaaaaaaa'以在运行时节省几个时钟周期。常量折叠仅发生在长度小于21的字符串中。(为什么?想象一下……的大小.pyc作为表达式的结果生成的文件'a'*10**10)。Here’s相同的实现源
  • 注意:在Python3.7中,常量折叠从窥视优化器移到了新的AST优化器,但在逻辑上也做了一些更改,因此第四个代码片段不适用于Python3.7。您可以阅读有关更改的更多信息here

▶注意链式操作

>>> (False == False) in [False] # makes sense
False
>>> False == (False in [False]) # makes sense
False
>>> False == False in [False] # now what?
True

>>> True is False == False
False
>>> False is False is False
True

>>> 1 > 0 < 1
True
>>> (1 > 0) < 1
False
>>> 1 > (0 < 1)
False

💡说明:

按规定https://docs.python.org/3/reference/expressions.html#membership-test-operations

形式上,如果a、b、c、…、y、z是表达式,并且op1、op2、…、opn是比较运算符,则a op1b、op2c。y opn z等同于a op1b和b op2c。y opn z,只是每个表达式最多求值一次

虽然在上面的示例中,这样的行为在您看来可能很愚蠢,但对于像这样的东西来说,它是非常棒的a == b == c0 <= x <= 100

  • False is False is False相当于(False is False) and (False is False)
  • True is False == False相当于True is False and False == False由于声明的第一部分(True is False)的计算结果为False,则整个表达式的计算结果为False
  • 1 > 0 < 1相当于1 > 0 and 0 < 1,它的计算结果为True
  • 表达式(1 > 0) < 1相当于True < 1

    >>> int(True)
    1
    >>> True + 1 #not relevant for this example, but just for fun
    2

    所以,1 < 1计算结果为False


▶如何不使用is操作员

下面是一个在互联网上流传的非常著名的例子。

1个

>>> a = 256
>>> b = 256
>>> a is b
True

>>> a = 257
>>> b = 257
>>> a is b
False

2个

>>> a = []
>>> b = []
>>> a is b
False

>>> a = tuple()
>>> b = tuple()
>>> a is b
True

3个输出

>>> a, b = 257, 257
>>> a is b
True

输出(特别是Python 3.7.x)

>>> a, b = 257, 257
>> a is b
False

💡说明:

两者之间的区别is==

  • is运算符检查两个操作数是否引用同一对象(即,它检查操作数的标识是否匹配)
  • ==运算符比较两个操作数的值并检查它们是否相同
  • 所以is是为了引用相等和==是为了价值平等。这是一个澄清问题的例子,

    >>> class A: pass
    >>> A() is A() # These are two empty objects at two different memory locations.
    False

256是现有对象,但257不是吗

当您启动python时,来自-5256将会被分配。这些数字用得很多,所以只要准备好就行了

报价自https://docs.python.org/3/c-api/long.html

当前的实现为-5到256之间的所有整数保留了一个整数对象数组,当您在该范围内创建一个int时,您只会得到对现有对象的引用。所以应该可以更改1的值。我怀疑Python的行为(在本例中)是未定义的。:-)

>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312
>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344

在这里,解释器在执行时不够聪明y = 257要认识到我们已经创建了一个值的整数257,因此,它继续在内存中创建另一个对象

类似的优化也适用于其他不可变的对象也喜欢空元组。由于列表是可变的,这就是为什么[] is []会回来的False() is ()会回来的True这解释了我们的第二个片段。让我们继续第三个问题,

两者都有ab在同一行中使用相同的值初始化时引用相同的对象

输出

>>> a, b = 257, 257
>>> id(a)
140640774013296
>>> id(b)
140640774013296
>>> a = 257
>>> b = 257
>>> id(a)
140640774013392
>>> id(b)
140640774013488
  • 当a和b设置为257在同一行中,Python解释器创建一个新对象,然后同时引用第二个变量。如果您在单独的行上执行,它不会“知道”已经有257作为一个对象
  • 它是一种编译器优化,特别适用于交互式环境。当您在实时解释器中输入两行时,它们被单独编译,因此分别进行了优化。如果您要在.py文件时,您将不会看到相同的行为,因为该文件是一次性编译的。这种优化并不局限于整数,它也适用于其他不可变的数据类型,如字符串(请查看“字符串是棘手的示例”)和浮点数。

    >>> a, b = 257.0, 257.0
    >>> a is b
    True
  • 为什么这不适用于Python3.7?抽象原因是因为这样的编译器优化是特定于实现的(即可能随版本、OS等而改变)。我还在找出是什么具体的实现更改导致了这个问题,您可以查看以下内容issue用于更新

▶哈希布朗尼

1个

some_dict = {}
some_dict[5.5] = "JavaScript"
some_dict[5.0] = "Ruby"
some_dict[5] = "Python"

输出:

>>> some_dict[5.5]
"JavaScript"
>>> some_dict[5.0] # "Python" destroyed the existence of "Ruby"?
"Python"
>>> some_dict[5] 
"Python"

>>> complex_five = 5 + 0j
>>> type(complex_five)
complex
>>> some_dict[complex_five]
"Python"

那么,为什么到处都是Python呢?

💡解释

  • Python字典中键的唯一性由等价性,而不是身份。所以即使是这样55.0,以及5 + 0j是不同类型的不同对象,因为它们是相等的,所以它们不可能都在同一个dict(或set)。一旦您插入其中的任何一个,尝试查找任何不同但等价的键将使用原始映射值成功(而不是使用KeyError):

    >>> 5 == 5.0 == 5 + 0j
    True
    >>> 5 is not 5.0 is not 5 + 0j
    True
    >>> some_dict = {}
    >>> some_dict[5.0] = "Ruby"
    >>> 5.0 in some_dict
    True
    >>> (5 in some_dict) and (5 + 0j in some_dict)
    True
  • 这在设置项目时也适用。所以当你这么做的时候some_dict[5] = "Python"时,Python会查找具有等效键的现有项5.0 -> "Ruby",在原地覆盖其值,而不使用原始键。

    >>> some_dict
    {5.0: 'Ruby'}
    >>> some_dict[5] = "Python"
    >>> some_dict
    {5.0: 'Python'}
  • 那么我们如何更新密钥以5(而不是5.0)?我们实际上不能就地进行此更新,但我们可以做的是首先删除密钥(del some_dict[5.0]),然后设置它(some_dict[5])以获取整数5作为密钥,而不是浮动5.0,尽管在极少数情况下应该需要这样做
  • Python是如何找到5在包含以下内容的词典中5.0?Python在固定时间内完成此操作,而不必使用散列函数扫描每一项。当Python查找密钥时foo在字典中,它首先计算hash(foo)(它以恒定时间运行)。因为在Python中要求比较相等的对象也具有相同的散列值(docs这里),55.0,以及5 + 0j具有相同的哈希值

    >>> 5 == 5.0 == 5 + 0j
    True
    >>> hash(5) == hash(5.0) == hash(5 + 0j)
    True

    注:反之亦然:散列值相等的对象本身可能不相等。(这导致了所谓的hash collision,并且降低了散列通常提供的恒定时间性能。)


▶在内心深处,我们都是一样的

class WTF:
  pass

输出:

>>> WTF() == WTF() # two different instances can't be equal
False
>>> WTF() is WTF() # identities are also different
False
>>> hash(WTF()) == hash(WTF()) # hashes _should_ be different as well
True
>>> id(WTF()) == id(WTF())
True

💡说明:

  • 什么时候id时,Python创建了一个WTF类对象,并将其传递给id功能。这个id函数将其id(其内存位置),并丢弃该对象。该对象将被销毁
  • 当我们连续两次执行此操作时,Python也会将相同的内存位置分配给第二个对象。自(在CPython中)id使用内存位置作为对象ID,两个对象的ID相同
  • 因此,对象的id只在对象的生存期内是唯一的。在销毁对象之后,或在创建对象之前,其他对象可以具有相同的id
  • 但是为什么is运算符的计算结果为False?让我们看看这个片段

    class WTF(object):
      def __init__(self): print("I")
      def __del__(self): print("D")

    输出:

    >>> WTF() is WTF()
    I
    I
    D
    D
    False
    >>> id(WTF()) == id(WTF())
    I
    D
    I
    D
    True

    正如你可能注意到的,这些物品被销毁的顺序是造成这里的所有不同之处的原因


▶秩序中的混乱*

from collections import OrderedDict

dictionary = dict()
dictionary[1] = 'a'; dictionary[2] = 'b';

ordered_dict = OrderedDict()
ordered_dict[1] = 'a'; ordered_dict[2] = 'b';

another_ordered_dict = OrderedDict()
another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a';

class DictWithHash(dict):
    """
    A dict that also implements __hash__ magic.
    """
    __hash__ = lambda self: 0

class OrderedDictWithHash(OrderedDict):
    """
    An OrderedDict that also implements __hash__ magic.
    """
    __hash__ = lambda self: 0

输出

>>> dictionary == ordered_dict # If a == b
True
>>> dictionary == another_ordered_dict # and b == c
True
>>> ordered_dict == another_ordered_dict # then why isn't c == a ??
False

# We all know that a set consists of only unique elements,
# let's try making a set of these dictionaries and see what happens...

>>> len({dictionary, ordered_dict, another_ordered_dict})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

# Makes sense since dict don't have __hash__ implemented, let's use
# our wrapper classes.
>>> dictionary = DictWithHash()
>>> dictionary[1] = 'a'; dictionary[2] = 'b';
>>> ordered_dict = OrderedDictWithHash()
>>> ordered_dict[1] = 'a'; ordered_dict[2] = 'b';
>>> another_ordered_dict = OrderedDictWithHash()
>>> another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a';
>>> len({dictionary, ordered_dict, another_ordered_dict})
1
>>> len({ordered_dict, another_ordered_dict, dictionary}) # changing the order
2

这里发生什么事情?

💡说明:

  • 不及物性平等不成立的原因dictionaryordered_dictanother_ordered_dict是因为这种方式__eq__方法是在OrderedDict班级。从docs

    OrderedDict对象之间的相等性测试是顺序敏感的,并且实现为list(od1.items())==list(od2.items())之间的相等性测试OrderedDict对象和其他映射对象与常规字典一样不区分顺序

  • 这种行为平等的原因是它允许OrderedDict在使用常规字典的任何地方都要直接替换的对象
  • 好的,那么为什么更改订单会影响生成的set反对吗?答案只有一个,那就是缺乏不及物平等。由于集合是唯一元素的“无序”集合,因此插入元素的顺序应该无关紧要。但在这种情况下,这确实很重要。让我们把它分解一下,

    >>> some_set = set()
    >>> some_set.add(dictionary) # these are the mapping objects from the snippets above
    >>> ordered_dict in some_set
    True
    >>> some_set.add(ordered_dict)
    >>> len(some_set)
    1
    >>> another_ordered_dict in some_set
    True
    >>> some_set.add(another_ordered_dict)
    >>> len(some_set)
    1
    
    >>> another_set = set()
    >>> another_set.add(ordered_dict)
    >>> another_ordered_dict in another_set
    False
    >>> another_set.add(another_ordered_dict)
    >>> len(another_set)
    2
    >>> dictionary in another_set
    True
    >>> another_set.add(another_ordered_dict)
    >>> len(another_set)
    2

    所以不一致是因为another_ordered_dict in another_set存在False因为ordered_dict已经出现在another_set正如之前观察到的,ordered_dict == another_ordered_dictFalse


▶继续努力。*

def some_func():
    try:
        return 'from_try'
    finally:
        return 'from_finally'

def another_func(): 
    for _ in range(3):
        try:
            continue
        finally:
            print("Finally!")

def one_more_func(): # A gotcha!
    try:
        for i in range(3):
            try:
                1 / i
            except ZeroDivisionError:
                # Let's throw it here and handle it outside for loop
                raise ZeroDivisionError("A trivial divide by zero error")
            finally:
                print("Iteration", i)
                break
    except ZeroDivisionError as e:
        print("Zero division error occurred", e)

输出:

>>> some_func()
'from_finally'

>>> another_func()
Finally!
Finally!
Finally!

>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

>>> one_more_func()
Iteration 0

💡说明:

  • 当一个returnbreakcontinue语句在try一套“Try…Finally”语句,finally子句也在退出时执行。
  • 函数的返回值由最后一个return语句已执行。由于finally子句始终执行,则会引发return中执行的语句finally子句将始终是最后执行的子句。
  • 这里需要注意的是,如果Finally子句执行returnbreak语句,则会丢弃临时保存的异常。

▶为了什么?

some_string = "wtf"
some_dict = {}
for i, some_dict[i] in enumerate(some_string):
    i = 10

输出:

>>> some_dict # An indexed dict appears.
{0: 'w', 1: 't', 2: 'f'}

💡说明:

  • 一个for语句在Python grammar作为:

    for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
    

    哪里exprlist是分配目标。这意味着相当于{exprlist} = {next_value}为每个项目执行在可迭代中。一个有趣的例子说明了这一点:

    for i in range(4):
        print(i)
        i = 10

    输出:

    0
    1
    2
    3
    

    您是否希望循环只运行一次?

    💡说明:

    • 赋值语句i = 10由于for循环在Python中的工作方式,永远不会影响循环的迭代。在每次迭代开始之前,迭代器提供的下一项(range(4)在本例中)被解包并分配给目标列表变量(i在这种情况下)
  • 这个enumerate(some_string)函数会产生一个新值。i(计数器向上)和来自some_string在每一次迭代中。然后它设置(刚刚分配的)i词典的关键字some_dict给那个角色。循环的展开可以简化为:

    >>> i, some_dict[i] = (0, 'w')
    >>> i, some_dict[i] = (1, 't')
    >>> i, some_dict[i] = (2, 'f')
    >>> some_dict

▶评估时间差异

1个

array = [1, 8, 15]
# A typical generator expression
gen = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]

输出:

>>> print(list(gen)) # Where did the other values go?
[8]

2个

array_1 = [1,2,3,4]
gen_1 = (x for x in array_1)
array_1 = [1,2,3,4,5]

array_2 = [1,2,3,4]
gen_2 = (x for x in array_2)
array_2[:] = [1,2,3,4,5]

输出:

>>> print(list(gen_1))
[1, 2, 3, 4]

>>> print(list(gen_2))
[1, 2, 3, 4, 5]

3个

array_3 = [1, 2, 3]
array_4 = [10, 20, 30]
gen = (i + j for i in array_3 for j in array_4)

array_3 = [4, 5, 6]
array_4 = [400, 500, 600]

输出:

>>> print(list(gen))
[401, 501, 601, 402, 502, 602, 403, 503, 603]

💡解释

  • 在一个generator表达式,则in子句在声明时求值,但条件子句在运行时求值
  • 所以在运行之前,array被重新分配到列表中[2, 8, 22],并且由于不在1815,只有8大于0,发电机只会产生8
  • 其产量的不同之处在于g1g2第二部分是因应方式变量array_1array_2是重新赋值的
  • 在第一种情况下,array_1绑定到新对象[1,2,3,4,5]而且由于in子句在声明时求值,它仍然引用旧对象。[1,2,3,4](未销毁)
  • 在第二种情况下,将切片分配给array_2更新相同的旧对象[1,2,3,4][1,2,3,4,5]因此,这两个g2array_2仍然有对同一对象的引用(该对象现在已更新为[1,2,3,4,5])
  • 好的,按照到目前为止讨论的逻辑,不应该是list(gen)在第三个片段中[11, 21, 31, 12, 22, 32, 13, 23, 33]?(因为array_3array_4会表现得就像array_1)。原因(仅限)array_4有关更新的值的说明,请参阅PEP-289

    仅立即计算最外层的for-expression,其他表达式将推迟到生成器运行


is not ...不是is (not ...)

>>> 'something' is not None
True
>>> 'something' is (not None)
False

💡解释

  • is not是单个二元运算符,其行为与使用isnot分开的
  • is not计算结果为False如果运算符两侧的变量指向同一对象,并且True否则
  • 在该示例中,(not None)计算结果为True因为它的价值NoneFalse在布尔上下文中,因此表达式变为'something' is True

▶一个X在第一次尝试中就赢了的井字棋(tic-tac-toe)!

# Let's initialize a row
row = [""] * 3 #row i['', '', '']
# Let's make a board
board = [row] * 3

输出:

>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]

我们没有分配三个"X"S,我们有吗?

💡说明:

当我们初始化时row变量,这种可视化解释了内存中发生的事情

image

而当board通过将row,这就是内存中发生的事情(每个元素board[0]board[1]board[2]是对由引用的同一列表的引用row)

image

我们可以在这里避免这种情况,方法是不使用row要生成的变量board(被问及this问题)

>>> board = [['']*3 for _ in range(3)]
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['', '', ''], ['', '', '']]

▶薛定谔变量*

funcs = []
results = []
for x in range(7):
    def some_func():
        return x
    funcs.append(some_func)
    results.append(some_func())  # note the function call here

funcs_results = [func() for func in funcs]

输出(Python版本):

>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]

的价值x在追加之前的每个迭代中都是不同的some_funcfuncs,但是在循环完成后对所有函数求值时,所有函数都返回6

>>> powers_of_x = [lambda x: x**i for i in range(10)]
>>> [f(2) for f in powers_of_x]
[512, 512, 512, 512, 512, 512, 512, 512, 512, 512]

💡说明:

  • 在循环内定义在其主体中使用循环变量的函数时,循环函数的闭包将绑定到变量,而不是ITS价值该函数查找x在周围的上下文中,而不是使用x在创建函数时。因此,所有函数都使用分配给变量的最新值进行计算。我们可以看到它正在使用x从周围的上下文(即局部变量),具有:

>>> import inspect
>>> inspect.getclosurevars(funcs[0])
ClosureVars(nonlocals={}, globals={'x': 6}, builtins={}, unbound=set())

因为x是全局值,我们可以更改funcs将通过更新查找并返回x

>>> x = 42
>>> [func() for func in funcs]
[42, 42, 42, 42, 42, 42, 42]
  • 要获得所需的行为,可以将循环变量作为命名变量传递给函数。为什么这个管用呢?因为这将定义变量内部函数的作用域。它将不再转到周围的(全局)作用域来查找变量值,而是创建一个局部变量来存储x在那个时间点上

funcs = []
for x in range(7):
    def some_func(x=x):
        return x
    funcs.append(some_func)

输出:

>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]

它不再使用x在全局范围内:

>>> inspect.getclosurevars(funcs[0])
ClosureVars(nonlocals={}, globals={}, builtins={}, unbound=set())

▶先有鸡还是先有蛋的问题**

1个

>>> isinstance(3, int)
True
>>> isinstance(type, object)
True
>>> isinstance(object, type)
True

那么,哪个是“终极”基类呢?顺便说一下,念力还有更多内容,

2个

>>> class A: pass
>>> isinstance(A, A)
False
>>> isinstance(type, type)
True
>>> isinstance(object, object)
True

3个

>>> issubclass(int, object)
True
>>> issubclass(type, object)
True
>>> issubclass(object, type)
False

💡解释

  • type是一种metaclass在Python中
  • 所有的一切是一种object在Python中,包括类及其对象(实例)
  • 班级type是类的元类object,以及每个班级(包括type)直接或间接继承自object
  • 中没有真正的基类。objecttype上述片段中的念力之所以出现,是因为我们正在考虑这些关系(issubclassisinstance)在Python类方面。两国之间的关系objecttype不能用纯python复制。更准确地说,以下关系不能在纯Python中重现,
    • 类A是类B的实例,类B是类A的实例
    • A类是其自身的一个实例
  • 这些关系之间的关系objecttype(两者既是彼此的实例,也是自己的实例)存在于Python中,因为在实现级别上存在“欺骗”

▶子类关系

输出:

>>> from collections import Hashable
>>> issubclass(list, object)
True
>>> issubclass(object, Hashable)
True
>>> issubclass(list, Hashable)
False

子类关系应该是可传递的,对吗?(即,如果A是的子类B,以及B是的子类C,即A应该的子类C)

💡说明:

  • 在Python中,子类关系不一定是可传递的。任何人都可以定义他们自己的,武断的__subclasscheck__在元类中
  • 什么时候issubclass(cls, Hashable)被调用,它只是简单地查找非Falsey“__hash__“中的方法cls或它继承的任何东西
  • 因为object是可以哈希的,但是list是不可散列的,它打破了传递性关系
  • 可以找到更详细的解释here

▶方法的等价性和同一性

class SomeClass:
    def method(self):
        pass

    @classmethod
    def classm(cls):
        pass

    @staticmethod
    def staticm():
        pass

输出:

>>> print(SomeClass.method is SomeClass.method)
True
>>> print(SomeClass.classm is SomeClass.classm)
False
>>> print(SomeClass.classm == SomeClass.classm)
True
>>> print(SomeClass.staticm is SomeClass.staticm)
True

访问classm两次,我们得到一个相等的对象,但不是相同的一?让我们看看如何处理以下实例SomeClass

o1 = SomeClass()
o2 = SomeClass()

输出:

>>> print(o1.method == o2.method)
False
>>> print(o1.method == o1.method)
True
>>> print(o1.method is o1.method)
False
>>> print(o1.classm is o1.classm)
False
>>> print(o1.classm == o1.classm == o2.classm == SomeClass.classm)
True
>>> print(o1.staticm is o1.staticm is o2.staticm is SomeClass.staticm)
True

访问 classmmethod两次,创建相等但不相等相同的对象的同一实例的SomeClass

💡解释

  • 函数有descriptors无论何时将函数作为属性访问,都会调用描述符,从而创建一个方法对象,该对象将函数与拥有该属性的对象“绑定”在一起。如果被调用,该方法调用函数,将绑定对象作为第一个参数隐式传递(这就是我们如何获取self作为第一个参数,尽管没有显式传递)

>>> o1.method
<bound method SomeClass.method of <__main__.SomeClass object at ...>>
  • 多次访问该属性每次都会创建一个方法对象!因此,o1.method is o1.method从来都不是真实的。但是,将函数作为类属性访问(与实例相反)并不会创建方法;因此SomeClass.method is SomeClass.method是真实的吗?

>>> SomeClass.method
<function SomeClass.method at ...>
  • classmethod将函数转换为类方法。类方法是描述符,当访问这些描述符时,会创建一个方法对象,该对象将班级对象的(类型),而不是对象本身

>>> o1.classm
<bound method SomeClass.classm of <class '__main__.SomeClass'>>
  • 与函数不同,classmethod在作为类属性访问时,也将创建一个方法(在这种情况下,它们绑定类,而不是绑定到类的类型)。所以SomeClass.classm is SomeClass.classm是假的

>>> SomeClass.classm
<bound method SomeClass.classm of <class '__main__.SomeClass'>>
  • 当两个函数相等且绑定对象相同时,方法对象会比较相等。所以o1.method == o1.method是真实的,尽管在内存中不是同一对象
  • staticmethod将函数转换为“no-op”描述符,该描述符按原样返回函数。不会创建任何方法对象,因此与is是真实的吗?

>>> o1.staticm
<function SomeClass.staticm at ...>
>>> SomeClass.staticm
<function SomeClass.staticm at ...>
  • 每次Python调用实例方法时都必须创建新的“方法”对象,并且每次都必须修改参数才能插入self严重影响了性能。CPython3.7solved it通过引入新的操作码来处理调用方法,而无需创建临时方法对象。这仅在实际调用所访问的函数时使用,因此此处的代码段不受影响,并且仍会生成方法:)

▶全真*

>>> all([True, True, True])
True
>>> all([True, True, False])
False

>>> all([])
True
>>> all([[]])
False
>>> all([[[]]])
True

为什么会有这种真假的改变呢?

💡说明:

  • 该计划的实施all函数等效于
  • def all(iterable):
        for element in iterable:
            if not element:
                return False
        return True
  • all([])退货True由于迭代器为空
  • all([[]])退货False因为传递的数组有一个元素,[],在python中,空列表是虚假的。
  • all([[[]]])更高的递归变体总是True这是因为传递的数组的单个元素([[...]])不再为空,带有值的列表为真

▶令人惊讶的逗号

输出(<3.6):

>>> def f(x, y,):
...     print(x, y)
...
>>> def g(x=4, y=5,):
...     print(x, y)
...
>>> def h(x, **kwargs,):
  File "<stdin>", line 1
    def h(x, **kwargs,):
                     ^
SyntaxError: invalid syntax

>>> def h(*args,):
  File "<stdin>", line 1
    def h(*args,):
                ^
SyntaxError: invalid syntax

💡说明:

  • 在Python函数的形参列表中,尾随逗号并不总是合法的
  • 在Python中,参数列表部分使用前导逗号定义,部分使用尾随逗号定义。此冲突会导致逗号被困在中间的情况,并且没有规则接受它
  • 注:后面的逗号问题是fixed in Python 3.6中的评论this简要讨论Python中尾随逗号的不同用法

▶字符串和反斜杠

输出:

>>> print("\"")
"

>>> print(r"\"")
\"

>>> print(r"\")
File "<stdin>", line 1
    print(r"\")
              ^
SyntaxError: EOL while scanning string literal

>>> r'\'' == "\\'"
True

💡解释

  • 在通常的python字符串中,反斜杠用于转义可能具有特殊含义的字符(如单引号、双引号和反斜杠本身)。

    >>> "wt\"f"
    'wt"f'
  • 在原始字符串文字中(由前缀指示r),则反斜杠会按原样传递自身,同时转义以下字符的行为

    >>> r'wt\"f' == 'wt\\"f'
    True
    >>> print(repr(r'wt\"f')
    'wt\\"f'
    
    >>> print("\n")
    
    >>> print(r"\\n")
    '\\n'
  • 这意味着当解析器在原始字符串中遇到反斜杠时,它会期待后面跟着另一个字符。在我们的情况下(print(r"\")),则反斜杠转义尾部引号,使解析器没有终止引号(因此SyntaxError)。这就是原始字符串末尾不能使用反斜杠的原因

▶不是结!

x = True
y = False

输出:

>>> not x == y
True
>>> x == not y
  File "<input>", line 1
    x == not y
           ^
SyntaxError: invalid syntax

💡说明:

  • 运算符优先级影响表达式的求值方式,并且==运算符的优先级高于notPython中的运算符
  • 所以not x == y相当于not (x == y)这相当于not (True == False)最终评估为True
  • x == not y引发一个SyntaxError因为它可以被认为等同于(x == not) y而不是x == (not y)这可能是你第一眼看到的
  • 解析器期望not令牌作为not in运算符(因为两者==not in运算符具有相同的优先级),但在无法找到in标记后跟在not令牌,则会引发SyntaxError

▶半个三引号字符串

输出:

>>> print('wtfpython''')
wtfpython
>>> print("wtfpython""")
wtfpython
>>> # The following statements raise `SyntaxError`
>>> # print('''wtfpython')
>>> # print("""wtfpython")
  File "<input>", line 3
    print("""wtfpython")
                        ^
SyntaxError: EOF while scanning triple-quoted string literal

💡说明:

  • Python支持隐式string literal concatenation,例如,

    >>> print("wtf" "python")
    wtfpython
    >>> print("wtf" "") # or "wtf"""
    wtf
    
  • '''"""也是Python中的字符串分隔符,这会导致语法错误,因为Python解释器在扫描当前遇到的三重引号字符串文字时期望使用终止的三重引号作为分隔符

▶布尔人有什么问题吗?

1个

# A simple example to count the number of booleans and
# integers in an iterable of mixed data types.
mixed_list = [False, 1.0, "some_string", 3, True, [], False]
integers_found_so_far = 0
booleans_found_so_far = 0

for item in mixed_list:
    if isinstance(item, int):
        integers_found_so_far += 1
    elif isinstance(item, bool):
        booleans_found_so_far += 1

输出:

>>> integers_found_so_far
4
>>> booleans_found_so_far
0

2个

>>> some_bool = True
>>> "wtf" * some_bool
'wtf'
>>> some_bool = False
>>> "wtf" * some_bool
''

3个

def tell_truth():
    True = False
    if True == False:
        print("I have lost faith in truth!")

输出(<3.x):

>>> tell_truth()
I have lost faith in truth!

💡说明:

  • bool是的子类int在Python中

    >>> issubclass(bool, int)
    True
    >>> issubclass(int, bool)
    False
  • 因此,TrueFalse是以下对象的实例int

    >>> isinstance(True, int)
    True
    >>> isinstance(False, int)
    True
  • 的整数值True1那就是False0

    >>> int(True)
    1
    >>> int(False)
    0
  • 查看此StackOverflowanswer以了解其背后的理论基础
  • 最初,Python过去没有bool类型(人们使用0表示FALSE,使用非零值1表示TRUE)。TrueFalse,和一个bool类型是在2.x版本中添加的,但是为了向后兼容,TrueFalse不能成为常量。它们只是内置变量,可以重新赋值
  • Python3向后不兼容,该问题最终被修复,因此最后一个代码片段不能与Python3.x一起工作!

▶类属性和实例属性

1个

class A:
    x = 1

class B(A):
    pass

class C(A):
    pass

输出:

>>> A.x, B.x, C.x
(1, 1, 1)
>>> B.x = 2
>>> A.x, B.x, C.x
(1, 2, 1)
>>> A.x = 3
>>> A.x, B.x, C.x # C.x changed, but B.x didn't
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)

2个

class SomeClass:
    some_var = 15
    some_list = [5]
    another_list = [5]
    def __init__(self, x):
        self.some_var = x + 1
        self.some_list = self.some_list + [x]
        self.another_list += [x]

输出:

>>> some_obj = SomeClass(420)
>>> some_obj.some_list
[5, 420]
>>> some_obj.another_list
[5, 420]
>>> another_obj = SomeClass(111)
>>> another_obj.some_list
[5, 111]
>>> another_obj.another_list
[5, 420, 111]
>>> another_obj.another_list is SomeClass.another_list
True
>>> another_obj.another_list is some_obj.another_list
True

💡说明:

  • 类变量和类实例中的变量作为类对象的字典在内部处理。如果在当前类的字典中找不到变量名,则会在父类中搜索该变量名
  • 这个+=运算符就地修改可变对象,而不创建新对象。因此,更改一个实例的属性会影响其他实例和类属性

▶一无所获

some_iterable = ('a', 'b')

def some_func(val):
    return "something"

输出(<=3.7.x):

>>> [x for x in some_iterable]
['a', 'b']
>>> [(yield x) for x in some_iterable]
<generator object <listcomp> at 0x7f70b0a4ad58>
>>> list([(yield x) for x in some_iterable])
['a', 'b']
>>> list((yield x) for x in some_iterable)
['a', None, 'b', None]
>>> list(some_func((yield x)) for x in some_iterable)
['a', 'something', 'b', 'something']

💡说明:


▶屈服于。回来!*

1个

def some_func(x):
    if x == 3:
        return ["wtf"]
    else:
        yield from range(x)

输出(>3.3):

>>> list(some_func(3))
[]

那辆车在哪里呢?"wtf"去?是不是因为有一些特殊的效果yield from?我们来验证一下,

2个

def some_func(x):
    if x == 3:
        return ["wtf"]
    else:
        for i in range(x):
          yield i

输出:

>>> list(some_func(3))
[]

同样的结果,这也不管用

💡说明:

  • 从Python3.3开始,可以使用return在生成器内具有值的语句(请参见PEP380)。这个official docs这么说吧,

“。”return expr在发电机中引起StopIteration(expr)在离开发电机时提升。“

  • 在以下情况下some_func(3)StopIteration是在一开始就提出的,因为return声明。这个StopIteration异常会自动捕获到list(...)包装器和for循环。因此,上述两个代码段将产生一个空列表
  • 为了得到["wtf"]从发电机some_func我们需要赶上StopIteration例外,

    try:
        next(some_func(3))
    except StopIteration as e:
        some_string = e.value

    >>> some_string
    ["wtf"]

▶NaN-自反性*

1个

a = float('inf')
b = float('nan')
c = float('-iNf')  # These strings are case-insensitive
d = float('nan')

输出:

>>> a
inf
>>> b
nan
>>> c
-inf
>>> float('some_other_string')
ValueError: could not convert string to float: some_other_string
>>> a == -c # inf==inf
True
>>> None == None # None == None
True
>>> b == d # but nan!=nan
False
>>> 50 / a
0.0
>>> a / a
nan
>>> 23 + b
nan

2个

>>> x = float('nan')
>>> y = x / x
>>> y is y # identity holds
True
>>> y == y # equality fails of y
False
>>> [y] == [y] # but the equality succeeds for the list containing y
True

💡说明:

  • 'inf''nan'是特殊字符串(不区分大小写),当显式类型转换为float类型,分别用于表示数学上的“无穷大”和“非数字”
  • 因为根据IEEE标准 NaN != NaN遵守此规则,则打破了Python中集合元素的自反性假设,即如果x是像这样的集合的一部分list,类似比较的实现是基于以下假设的x == x由于这一假设,在比较两个元素时,首先比较标识(因为这样更快),只有当标识不匹配时才比较值。下面的片段会让事情变得更清楚,

    >>> x = float('nan')
    >>> x == x, [x] == [x]
    (False, True)
    >>> y = float('nan')
    >>> y == y, [y] == [y]
    (False, True)
    >>> x == y, [x] == [y]
    (False, False)

    因为他们的身份xy是不同的,则考虑这些值,这些值也是不同的;因此比较返回False这一次

  • 有趣的阅读:Reflexivity, and other pillars of civilization

▶变异不变的东西!

如果您知道引用在Python中的工作方式,这可能看起来微不足道

some_tuple = ("A", "tuple", "with", "values")
another_tuple = ([1, 2], [3, 4], [5, 6])

输出:

>>> some_tuple[2] = "change this"
TypeError: 'tuple' object does not support item assignment
>>> another_tuple[2].append(1000) #This throws no error
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000])
>>> another_tuple[2] += [99, 999]
TypeError: 'tuple' object does not support item assignment
>>> another_tuple
([1, 2], [3, 4], [5, 6, 1000, 99, 999])

但是我认为元组是不变的

💡说明:

  • 报价自https://docs.python.org/3/reference/datamodel.html

    不可变序列不可变序列类型的对象一旦创建就不能更改。(如果对象包含对其他对象的引用,则这些其他对象可以是可变的,并且可以修改;但是,由不可变对象直接引用的对象集合不能更改。)

  • +=操作员就地更改列表。项目分配不起作用,但当异常发生时,项目已更改到位
  • 这里面也有一个解释official Python FAQ

▶外部作用域中正在消失的变量

e = 7
try:
    raise Exception()
except Exception as e:
    pass

输出(Python 2.x):

>>> print(e)
# prints nothing

输出(Python 3.x):

>>> print(e)
NameError: name 'e' is not defined

💡说明:

  • 来源:https://docs.python.org/3/reference/compound_stmts.html#except

    在使用以下命令分配异常时as目标,则在except条款。这就好像

    except E as N:
        foo

    被翻译成

    except E as N:
        try:
            foo
        finally:
            del N

    这意味着必须为异常指定不同的名称,才能在EXCEPT子句之后引用它。异常之所以被清除,是因为附加了回溯后,它们与堆栈帧形成一个引用循环,使该帧中的所有局部变量保持活动状态,直到下一次垃圾回收发生

  • 这些子句在Python中没有作用域。示例中的所有内容都在相同的作用域中,并且变量e由于执行except条款。对于具有独立内部作用域的函数则不是这样。下面的示例说明了这一点:

    def f(x):
        del(x)
        print(x)
    
    x = 5
    y = [5, 4, 3]

    输出:

    >>>f(x)
    UnboundLocalError: local variable 'x' referenced before assignment
    >>>f(y)
    UnboundLocalError: local variable 'x' referenced before assignment
    >>> x
    5
    >>> y
    [5, 4, 3]
  • 在Python 2.x中,变量名称e分配给Exception()实例,因此当您尝试打印时,它不打印任何内容

    输出(Python 2.x):

    >>> e
    Exception()
    >>> print e
    # Nothing is printed!

▶神秘的钥匙类型转换

class SomeClass(str):
    pass

some_dict = {'s': 42}

输出:

>>> type(list(some_dict.keys())[0])
str
>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict # expected: Two different keys-value pairs
{'s': 40}
>>> type(list(some_dict.keys())[0])
str

💡说明:

  • 这两个对象s和那根弦"s"散列为相同的值,因为SomeClass继承__hash__一种方法str班级
  • SomeClass("s") == "s"计算结果为True因为SomeClass还继承了__eq__方法来自str班级
  • 由于这两个对象散列为相同的值并且相等,因此它们在字典中由相同的键表示
  • 对于所需的行为,我们可以重新定义__eq__中的方法SomeClass

    class SomeClass(str):
      def __eq__(self, other):
          return (
              type(self) is SomeClass
              and type(other) is SomeClass
              and super().__eq__(other)
          )
    
      # When we define a custom __eq__, Python stops automatically inheriting the
      # __hash__ method, so we need to define it as well
      __hash__ = str.__hash__
    
    some_dict = {'s':42}

    输出:

    >>> s = SomeClass('s')
    >>> some_dict[s] = 40
    >>> some_dict
    {'s': 40, 's': 42}
    >>> keys = list(some_dict.keys())
    >>> type(keys[0]), type(keys[1])
    (__main__.SomeClass, str)

▶让我们看看你能不能猜到这个?

a, b = a[b] = {}, 5

输出:

>>> a
{5: ({...}, 5)}

💡说明:

赋值语句计算表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者生成一个元组),并将单个结果对象从左到右分配给每个目标列表

  • 这个+在……里面(target_list "=")+意味着可以有一个或多个目标列表。在本例中,目标列表为a, ba[b](请注意,表达式列表正好是一个,在我们的示例中是{}, 5)
  • 对表达式列表求值后,将其值解压缩到目标列表中从左到右因此,在我们的案例中,首先{}, 5将元组解包为a, b现在我们有了a = {}b = 5
  • a现在分配给{},它是一个可变对象
  • 第二个目标列表是a[b](您可能认为这会抛出一个错误,因为ab在以前的语句中没有定义。但请记住,我们刚刚分配了a{}b5)
  • 现在,我们正在设置密钥5在字典中设置为元组({}, 5)创建循环引用({...}在输出中引用的对象与a已在引用)。循环引用的另一个更简单的示例可以是

    >>> some_list = some_list[0] = [0]
    >>> some_list
    [[...]]
    >>> some_list[0]
    [[...]]
    >>> some_list is some_list[0]
    True
    >>> some_list[0][0][0][0][0][0] == some_list
    True

    我们示例中的情况与此类似(a[b][0]是与以下对象相同的对象a)

  • 因此,总而言之,您可以将该示例分解为

    a, b = {}, 5
    a[b] = a, b

    循环引用可以用以下事实来证明a[b][0]是与以下对象相同的对象a

    >>> a[b][0] is a
    True


剖面:湿滑斜坡

▶在迭代字典时修改字典

x = {0: None}

for i in x:
    del x[i]
    x[i+1] = None
    print(i)

输出(Python 2.7-Python 3.5):

0
1
2
3
4
5
6
7

是的,它的运行时间正好是时间和停靠站

💡说明:

  • 不支持对同时编辑的字典进行迭代
  • 它运行8次,因为这是字典调整大小以容纳更多键的时间点(我们有8个删除条目,因此需要调整大小)。这实际上是一个实现细节
  • 对于不同的Python实现,处理已删除键的方式和调整大小的时间可能会有所不同
  • 因此,对于除Python2.7-Python3.5之外的Python版本,计数可能不同于8(但无论计数是多少,每次运行它都是一样的)。你可以找到一些关于这方面的讨论here或在this堆栈溢出线程
  • 从Python 3.7.6开始,您将看到RuntimeError: dictionary keys changed during iteration如果您尝试执行此操作,则会出现异常

▶固执的del运营

class SomeClass:
    def __del__(self):
        print("Deleted!")

输出:1个

>>> x = SomeClass()
>>> y = x
>>> del x # this should print "Deleted!"
>>> del y
Deleted!

哎呀,终于删掉了。你可能已经猜到是什么救了你__del__在我们第一次尝试删除时被调用x让我们在这个示例中添加更多的曲折

2个

>>> x = SomeClass()
>>> y = x
>>> del x
>>> y # check if y exists
<__main__.SomeClass instance at 0x7f98a1a67fc8>
>>> del y # Like previously, this should print "Deleted!"
>>> globals() # oh, it didn't. Let's check all our global variables and confirm
Deleted!
{'__builtins__': <module '__builtin__' (built-in)>, 'SomeClass': <class __main__.SomeClass at 0x7f98a1a5f668>, '__package__': None, '__name__': '__main__', '__doc__': None}

好的,现在它被删除了😕

💡说明:

  • del x不会直接调用x.__del__()
  • 什么时候del x时,Python将删除该名称x从当前作用域开始,并将对象的引用计数减1x已引用。__del__()仅当对象的引用计数达到零时才调用
  • 在第二个输出片段中,__del__()未调用,因为前面的语句(>>> y)创建了对同一对象的另一个引用(具体地说,_魔术变量,它引用最后一个非None表达式),从而防止在以下情况下引用计数达到零del y遇到了
  • 呼叫globals(或者实际上,执行任何将具有非None结果)导致_若要引用新结果,请删除现有引用。现在引用计数达到0,我们可以看到“已删除!”正在打印中(终于!)

▶超出作用域的变量

1个

a = 1
def some_func():
    return a

def another_func():
    a += 1
    return a

2个

def some_closure_func():
    a = 1
    def some_inner_func():
        return a
    return some_inner_func()

def another_closure_func():
    a = 1
    def another_inner_func():
        a += 1
        return a
    return another_inner_func()

输出:

>>> some_func()
1
>>> another_func()
UnboundLocalError: local variable 'a' referenced before assignment

>>> some_closure_func()
1
>>> another_closure_func()
UnboundLocalError: local variable 'a' referenced before assignment

💡说明:

  • 当您为作用域中的变量赋值时,它将变为该作用域的局部变量。所以a成为本地化的作用域another_func,但是它以前没有在相同的作用域中初始化,这会引发错误
  • 修改外部作用域变量a在……里面another_func,我们必须使用global关键字

    def another_func()
        global a
        a += 1
        return a

    输出:

    >>> another_func()
    2
  • 在……里面another_closure_funca成为本地化的作用域another_inner_func,但是它以前没有在相同的作用域中初始化,这就是它抛出错误的原因。
  • 修改外部作用域变量a在……里面another_inner_func,请使用nonlocal关键字。非本地语句用于引用在最近的外部(不包括全局)作用域中定义的变量

    def another_func():
        a = 1
        def another_inner_func():
            nonlocal a
            a += 1
            return a
        return another_inner_func()

    输出:

    >>> another_func()
    2
  • 关键字globalnonlocal告诉python解释器不要声明新变量,并在相应的外部作用域中查找它们。
  • 朗读this这是一本简短但令人敬畏的指南,可帮助您详细了解Python中的名称空间和作用域解析是如何工作的

▶迭代时删除列表项

list_1 = [1, 2, 3, 4]
list_2 = [1, 2, 3, 4]
list_3 = [1, 2, 3, 4]
list_4 = [1, 2, 3, 4]

for idx, item in enumerate(list_1):
    del item

for idx, item in enumerate(list_2):
    list_2.remove(item)

for idx, item in enumerate(list_3[:]):
    list_3.remove(item)

for idx, item in enumerate(list_4):
    list_4.pop(idx)

输出:

>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]

你能猜出为什么输出是[2, 4]

💡说明:

  • 更改正在迭代的对象从来都不是一个好主意。这样做的正确方法是迭代对象的副本,并且list_3[:]就是这么做的吗?

    >>> some_list = [1, 2, 3, 4]
    >>> id(some_list)
    139798789457608
    >>> id(some_list[:]) # Notice that python creates new object for sliced list.
    139798779601192

两者之间的差异delremove,以及pop

  • del var_name只是移除了var_name从本地或全局命名空间(这就是为什么list_1不受影响)
  • remove移除第一个匹配值,而不是特定索引,将引发ValueError如果找不到该值
  • pop移除特定索引处的元素并将其返回,引发IndexError如果指定的索引无效

为什么输出是[2, 4]

  • 列表迭代是逐个索引完成的,当我们删除1从…list_2list_4,列表的内容现在是[2, 3, 4]剩余的元素被下移,即,2位于索引0,并且3由于下一次迭代将查看索引1(它是3)、2完全跳过了。列表序列中的每个备用元素都会发生类似的情况
  • 请参阅此StackOverflowthread解释示例
  • 另请参见这个不错的StackOverflowthread查看与Python中的字典相关的类似示例

▶迭代程序的有损压缩*

>>> numbers = list(range(7))
>>> numbers
[0, 1, 2, 3, 4, 5, 6]
>>> first_three, remaining = numbers[:3], numbers[3:]
>>> first_three, remaining
([0, 1, 2], [3, 4, 5, 6])
>>> numbers_iter = iter(numbers)
>>> list(zip(numbers_iter, first_three)) 
[(0, 0), (1, 1), (2, 2)]
# so far so good, let's zip the remaining
>>> list(zip(numbers_iter, remaining))
[(4, 3), (5, 4), (6, 5)]

DID元素位于何处3numbers名单?

💡说明:

  • 来自Pythondocs,这里是zip函数的大致实现,

    def zip(*iterables):
        sentinel = object()
        iterators = [iter(it) for it in iterables]
        while iterators:
            result = []
            for it in iterators:
                elem = next(it, sentinel)
                if elem is sentinel: return
                result.append(elem)
            yield tuple(result)
  • 因此,该函数接受任意数量的可迭代对象,并将它们的每个项添加到result列表,方法是调用next函数,并在任何迭代量耗尽时停止
  • 这里需要注意的是,当耗尽任何可迭代时,result列表将被丢弃。这就是发生在3numbers_iter
  • 执行上述操作的正确方法是使用zip会是,

    >>> numbers = list(range(7))
    >>> numbers_iter = iter(numbers)
    >>> list(zip(first_three, numbers_iter))
    [(0, 0), (1, 1), (2, 2)]
    >>> list(zip(remaining, numbers_iter))
    [(3, 3), (4, 4), (5, 5), (6, 6)]

    zip的第一个参数应该是元素最少的那个


▶循环变量泄漏!

1个

for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

输出:

6 : for x inside loop
6 : x in global

x从未在for循环的作用域之外定义

2个

# This time let's initialize x first
x = -1
for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

输出:

6 : for x inside loop
6 : x in global

3个

输出(Python 2.x):

>>> x = 1
>>> print([x for x in range(5)])
[0, 1, 2, 3, 4]
>>> print(x)
4

输出(Python 3.x):

>>> x = 1
>>> print([x for x in range(5)])
[0, 1, 2, 3, 4]
>>> print(x)
1

💡说明:

  • 在Python中,for循环使用它们所在的作用域,并将其定义的循环变量留在后面。如果我们之前在全局名称空间中显式定义了for-loop变量,这也适用。在这种情况下,它将重新绑定现有变量
  • 列表理解示例的Python 2.x和Python 3.x解释器的输出差异可通过中记录的以下更改进行解释What’s New In Python 3.0更改日志:

    “列表理解不再支持语法形式[... for var in item1, item2, ...]使用[... for var in (item1, item2, ...)]取而代之的是。还要注意,列表理解具有不同的语义:它们更接近于list()构造函数,特别是循环控制变量不再泄漏到周围的作用域。“


▶注意默认的可变参数!

def some_func(default_arg=[]):
    default_arg.append("some_string")
    return default_arg

输出:

>>> some_func()
['some_string']
>>> some_func()
['some_string', 'some_string']
>>> some_func([])
['some_string']
>>> some_func()
['some_string', 'some_string', 'some_string']

💡说明:

  • Python中函数的默认可变参数并不是在您每次调用函数时都真正初始化的。取而代之的是,使用最近分配给它们的值作为默认值。当我们显式地传递[]some_func作为参数,default_arg未使用变量,因此函数按预期返回

    def some_func(default_arg=[]):
        default_arg.append("some_string")
        return default_arg

    输出:

    >>> some_func.__defaults__ #This will show the default argument values for the function
    ([],)
    >>> some_func()
    >>> some_func.__defaults__
    (['some_string'],)
    >>> some_func()
    >>> some_func.__defaults__
    (['some_string', 'some_string'],)
    >>> some_func([])
    >>> some_func.__defaults__
    (['some_string', 'some_string'],)
  • 避免由于可变参数导致的错误的常见做法是将None作为默认值,稍后检查是否有任何值传递给与该参数对应的函数。示例:

    def some_func(default_arg=None):
        if default_arg is None:
            default_arg = []
        default_arg.append("some_string")
        return default_arg

▶捕捉异常

some_list = [1, 2, 3]
try:
    # This should raise an ``IndexError``
    print(some_list[4])
except IndexError, ValueError:
    print("Caught!")

try:
    # This should raise a ``ValueError``
    some_list.remove(4)
except IndexError, ValueError:
    print("Caught again!")

输出(Python 2.x):

Caught!

ValueError: list.remove(x): x not in list

输出(Python 3.x):

  File "<input>", line 3
    except IndexError, ValueError:
                     ^
SyntaxError: invalid syntax

💡解释

  • 要向EXCEPT子句添加多个异常,需要将它们作为带括号的元组作为第一个参数传递。第二个参数是一个可选名称,当提供该名称时,它将绑定已引发的异常实例。例如,

    some_list = [1, 2, 3]
    try:
       # This should raise a ``ValueError``
       some_list.remove(4)
    except (IndexError, ValueError), e:
       print("Caught again!")
       print(e)

    输出(Python 2.x):

    Caught again!
    list.remove(x): x not in list
    

    输出(Python 3.x):

      File "<input>", line 4
        except (IndexError, ValueError), e:
                                         ^
    IndentationError: unindent does not match any outer indentation level
  • 不建议使用逗号将异常与变量分开,这在Python3中不起作用;正确的方法是使用as例如,

    some_list = [1, 2, 3]
    try:
        some_list.remove(4)
    
    except (IndexError, ValueError) as e:
        print("Caught again!")
        print(e)

    输出:

    Caught again!
    list.remove(x): x not in list
    

▶同样的操作数,不同的故事!

1个

a = [1, 2, 3, 4]
b = a
a = a + [5, 6, 7, 8]

输出:

>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4]

2个

a = [1, 2, 3, 4]
b = a
a += [5, 6, 7, 8]

输出:

>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]

💡说明:

  • a += b并不总是以相同的方式表现为a = a + b班级可能实施op=运算符不同,列表就是这样做的
  • 表达式a = a + [5,6,7,8]生成新列表并设置a对新列表的引用,离开b不变
  • 表达式a += [5,6,7,8]实际上映射到在列表上操作的“扩展”函数,以便ab仍然指向已就地修改的同一列表

▶忽略类作用域的名称解析

1个

x = 5
class SomeClass:
    x = 17
    y = (x for i in range(10))

输出:

>>> list(SomeClass.y)[0]
5

2个

x = 5
class SomeClass:
    x = 17
    y = [x for i in range(10)]

输出(Python 2.x):

>>> SomeClass.y[0]
17

输出(Python 3.x):

>>> SomeClass.y[0]
5

💡解释

  • 嵌套在类定义内的作用域忽略类级别绑定的名称
  • 生成器表达式有其自己的作用域
  • 从Python3.x开始,列表理解也有自己的作用域

▶像银行家一样圆滑*

让我们实现一个朴素的函数来获取列表的中间元素:

def get_middle(some_list):
    mid_index = round(len(some_list) / 2)
    return some_list[mid_index - 1]

Python 3.x:

>>> get_middle([1])  # looks good
1
>>> get_middle([1,2,3])  # looks good
2
>>> get_middle([1,2,3,4,5])  # huh?
2
>>> len([1,2,3,4,5]) / 2  # good
2.5
>>> round(len([1,2,3,4,5]) / 2)  # why?
2

看起来Python似乎将2.5舍入为2

💡说明:

  • 这不是浮点精度错误,事实上,此行为是故意的。从Python3.0开始,round()用途banker’s rounding其中0.5个分数四舍五入到最接近的甚至编号:

>>> round(0.5)
0
>>> round(1.5)
2
>>> round(2.5)
2
>>> import numpy  # numpy does the same
>>> numpy.round(0.5)
0.0
>>> numpy.round(1.5)
2.0
>>> numpy.round(2.5)
2.0
  • 这是对0.5小数进行舍入的推荐方式,如中所述IEEE 754然而,另一种方式(从零开始四舍五入)大部分时间都是在学校教授的,所以银行家的舍入可能不是那么出名。此外,一些最流行的编程语言(例如:JavaScript、Java、C/C++、Ruby、Rust)也不使用银行家取整。因此,这对于Python语言来说仍然非常特殊,并且在对分数进行舍入时可能会导致念力
  • 请参阅round() docsthis stackoverflow thread了解更多信息
  • 请注意,get_middle([1])仅返回1,因为索引为round(0.5) - 1 = 0 - 1 = -1,返回列表中的最后一个元素

▶干草堆里的针*

到目前为止,我还没有遇到过一位体验过Pythonist的人,他没有遇到过以下一个或多个场景,

1个

x, y = (0, 1) if True else None, None

输出:

>>> x, y  # expected (0, 1)
((0, 1), None)

2个

t = ('one', 'two')
for i in t:
    print(i)

t = ('one')
for i in t:
    print(i)

t = ()
print(t)

输出:

one
two
o
n
e
tuple()

3个

ten_words_list = [
    "some",
    "very",
    "big",
    "list",
    "that"
    "consists",
    "of",
    "exactly",
    "ten",
    "words"
]

输出

>>> len(ten_words_list)
9

4.主张不够有力

a = "python"
b = "javascript"

输出:

# An assert statement with an assertion failure message.
>>> assert(a == b, "Both languages are different")
# No AssertionError is raised

5个

some_list = [1, 2, 3]
some_dict = {
  "key_1": 1,
  "key_2": 2,
  "key_3": 3
}

some_list = some_list.append(4) 
some_dict = some_dict.update({"key_4": 4})

输出:

>>> print(some_list)
None
>>> print(some_dict)
None

6个

def some_recursive_func(a):
    if a[0] == 0:
        return
    a[0] -= 1
    some_recursive_func(a)
    return a

def similar_recursive_func(a):
    if a == 0:
        return a
    a -= 1
    similar_recursive_func(a)
    return a

输出:

>>> some_recursive_func([5, 0])
[0, 0]
>>> similar_recursive_func(5)
4

💡说明:

  • 对于%1,预期行为的正确语句为x, y = (0, 1) if True else (None, None)
  • 对于2,预期行为的正确语句为t = ('one',)t = 'one',(缺少逗号)否则口译员会认为t成为一名str并逐个字符对其进行迭代
  • ()是一个特殊标记,表示为空tuple
  • 在3中,正如您可能已经知道的那样,第5个元素后面缺少逗号("that")。因此,通过隐式字符串文字连接,

    >>> ten_words_list
    ['some', 'very', 'big', 'list', 'thatconsists', 'of', 'exactly', 'ten', 'words']
  • 不是的AssertionError在第四个代码段中引发,因为不是断言单个表达式a == b,我们断言整个元组。下面的代码片断将澄清问题,

    >>> a = "python"
    >>> b = "javascript"
    >>> assert a == b
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    AssertionError
    
    >>> assert (a == b, "Values are not equal")
    <stdin>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
    
    >>> assert a == b, "Values are not equal"
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    AssertionError: Values are not equal
  • 对于第五个代码段,大多数修改序列/映射对象项的方法,如list.appenddict.updatelist.sort等,就地修改对象并返回None这背后的基本原理是,如果操作可以就地完成,则可以通过避免复制对象来提高性能(请参阅here)
  • 最后一个应该是相当明显的可变对象(如list)可以在函数中更改,并且重新分配不可变的(a -= 1)不是对价值的更改
  • 从长远来看,意识到这些吹毛求疵可以为您节省数小时的调试工作

▶分裂*

>>> 'a'.split()
['a']

# is same as
>>> 'a'.split(' ')
['a']

# but
>>> len(''.split())
0

# isn't the same as
>>> len(''.split(' '))
1

💡说明:

  • 最初可能显示分割的默认分隔符是单个空格' ',但根据docs

    如果未指定SEP或None,则应用不同的拆分算法:连续的空格串被视为单个分隔符,如果字符串具有前导空格或尾随空格,则结果的开头或结尾处将不包含空字符串。因此,拆分空字符串或仅由空格组成的字符串(使用NONE分隔符)将返回[]如果给定了SEP,则连续的分隔符不会组合在一起,并被视为分隔空字符串(例如,'1,,2'.split(',')退货['1', '', '2'])。使用指定的分隔符拆分空字符串将返回['']

  • 注意以下代码片段中前导空格和尾随空格的处理方式会让事情变得清晰起来,

    >>> ' a '.split(' ')
    ['', 'a', '']
    >>> ' a '.split()
    ['a']
    >>> ''.split(' ')
    ['']

▶野生进口**

# File: module.py

def some_weird_name_func_():
    print("works!")

def _another_weird_name_func():
    print("works!")

输出

>>> from module import *
>>> some_weird_name_func_()
"works!"
>>> _another_weird_name_func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_another_weird_name_func' is not defined

💡说明:

  • 通常建议不要使用通配符导入。第一个显而易见的原因是,在通配符导入中,不会导入带有前导下划线的名称。这可能会导致运行时出错
  • 如果我们用了from ... import a, b, c语法,以上NameError就不会发生

    >>> from module import some_weird_name_func_, _another_weird_name_func
    >>> _another_weird_name_func()
    works!
  • 如果您真的想使用通配符导入,那么您必须定义列表__all__在您的模块中,它将包含在执行通配符导入时可用的公共对象列表

    __all__ = ['_another_weird_name_func']
    
    def some_weird_name_func_():
        print("works!")
    
    def _another_weird_name_func():
        print("works!")

    输出

    >>> _another_weird_name_func()
    "works!"
    >>> some_weird_name_func_()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'some_weird_name_func_' is not defined

▶都整理好了吗*

>>> x = 7, 8, 9
>>> sorted(x) == x
False
>>> sorted(x) == sorted(x)
True

>>> y = reversed(x)
>>> sorted(y) == sorted(y)
False

💡说明:

  • 这个sorted方法始终返回列表,比较列表和元组始终返回False在Python中
  • >>> [] == tuple()
    False
    >>> x = 7, 8, 9
    >>> type(x), type(sorted(x))
    (tuple, list)
  • 不像sorted,即reversed方法返回迭代器。为什么?因为排序需要就地修改迭代器或使用额外的容器(列表),而反转只需从最后一个索引迭代到第一个索引即可
  • 所以在比较的时候sorted(y) == sorted(y),第一次调用sorted()将使用迭代器y,下一次调用将只返回一个空列表

    >>> x = 7, 8, 9
    >>> y = reversed(x)
    >>> sorted(y), sorted(y)
    ([7, 8, 9], [])

▶午夜时间不存在吗?

from datetime import datetime

midnight = datetime(2018, 1, 1, 0, 0)
midnight_time = midnight.time()

noon = datetime(2018, 1, 1, 12, 0)
noon_time = noon.time()

if midnight_time:
    print("Time at midnight is", midnight_time)

if noon_time:
    print("Time at noon is", noon_time)

输出(<3.5):

('Time at noon is', datetime.time(12, 0))

未打印午夜时间

💡说明:

在Python 3.5之前的版本中,datetime.time对象被认为是False如果它代表协调世界时的午夜。在使用if obj:语法,以检查是否obj为NULL或与“Empty”等价物。



部分:隐藏的宝藏!

这一节包含一些像我这样的初学者不知道的关于Python的鲜为人知和有趣的事情(好吧,现在不知道了)

▶好的,python,能给我做飞翔吗?

好的,给你

import antigravity

输出:嘘。这是个超级秘密

💡说明:

  • antigravity模块是Python开发人员发布的为数不多的复活节彩蛋之一
  • import antigravity打开Web浏览器,指向classic XKCD comic关于Python
  • 嗯,还有更多的原因。那里有复活节彩蛋里面的另一个复活节彩蛋如果你看一下code中定义了一个函数,该函数旨在实现XKCD’s geohashing algorithm

goto但是为什么呢?

from goto import goto, label
for i in range(9):
    for j in range(9):
        for k in range(9):
            print("I am trapped, please rescue!")
            if k == 2:
                goto .breakout # breaking out from a deeply nested loop
label .breakout
print("Freedom!")

输出(Python 2.3):

I am trapped, please rescue!
I am trapped, please rescue!
Freedom!

💡说明:

  • 的工作版本goto在Python中是announced作为2004年4月1日的愚人节笑话
  • 当前版本的Python没有此模块
  • 虽然有效,但请不要使用。这是reason为什么goto在Python中不存在

▶振作起来!

如果您不喜欢在Python中使用空格来表示作用域,您可以使用C样式{},方法是导入

from __future__ import braces

输出:

  File "some_file.py", line 1
    from __future__ import braces
SyntaxError: not a chance

牙套?不行!如果您认为这令人失望,可以使用Java。好的,另一件令人惊讶的事,你能找到SyntaxError成长于__future__模块code

💡说明:

  • 这个__future__模块通常用于提供未来版本的Python的功能。然而,在这一特定背景下的“未来”是具有讽刺意味的。
  • 这是一个复活节彩蛋,关注社区在这个问题上的感受
  • 代码实际上是存在的here在……里面future.c文件
  • 当CPython编译器遇到future statement,它首先在future.c在将其视为普通导入语句之前

▶让我们来见见友好的终生语言大叔

输出(Python 3.x)

>>> from __future__ import barry_as_FLUFL
>>> "Ruby" != "Python" # there's no doubt about it
  File "some_file.py", line 1
    "Ruby" != "Python"
              ^
SyntaxError: invalid syntax

>>> "Ruby" <> "Python"
True

好了,我们走吧

💡说明:

  • 这与以下内容相关PEP-4012009年4月1日上映(现在你知道这意味着什么了)
  • 引用PEP-401

    认识到Python3.0中的!=不等式运算符是一个可怕的、会导致手指疼痛的错误,FLUFL恢复了<>菱形运算符作为唯一拼写

  • 巴里叔叔在PEP中有更多的东西要分享;你可以阅读它们here
  • 它在交互环境中工作得很好,但它会引发SyntaxError当您通过python文件运行时(请参阅此issue)。但是,您可以将语句包装在evalcompile为了让它运转起来,

    from __future__ import barry_as_FLUFL
    print(eval('"Ruby" <> "Python"'))

▶即使是python也明白爱情是复杂的

import this

等等,这是什么this就是爱❤️

输出:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

这是巨蟒的禅宗!

>>> love = this
>>> this is love
True
>>> love is True
False
>>> love is False
False
>>> love is not True or False
True
>>> love is not True or False; love is love  # Love is complicated
True

💡说明:

  • thisPython中的模块是Python禅宗的复活节彩蛋(PEP 20)
  • 如果您认为这已经足够有趣,请查看this.py有趣的是,禅宗的密码违背了它自己(这可能是唯一发生这种情况的地方)
  • 关于这份声明love is not True or False; love is love,具有讽刺意味,但这是不言而喻的(如果不是,请参阅与以下内容相关的示例isis not操作员)

▶是的,它确实存在!

这个elseFOR循环子句一个典型的示例可能是:

  def does_exists_num(l, to_find):
      for num in l:
          if num == to_find:
              print("Exists!")
              break
      else:
          print("Does not exist")

输出:

>>> some_list = [1, 2, 3, 4, 5]
>>> does_exists_num(some_list, 4)
Exists!
>>> does_exists_num(some_list, -1)
Does not exist

这个else异常处理中的子句举个例子,

try:
    pass
except:
    print("Exception occurred!!!")
else:
    print("Try block executed successfully...")

输出:

Try block executed successfully...

💡说明:

  • 这个else循环后的子句仅在没有显式break在所有的迭代之后。你可以把它看作是“不中断”条款。
  • else挡路试水后条款又称“补全条款”,即到达else子句中的子句try语句表示试用挡路实际已成功完成

▶省略号*

def some_func():
    Ellipsis

输出

>>> some_func()
# No output, No Error

>>> SomeRandomString
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'SomeRandomString' is not defined

>>> Ellipsis
Ellipsis

💡解释

  • 在Python中,Ellipsis是全局可用的内置对象,它等效于...

    >>> ...
    Ellipsis
  • 省略可以用于几个目的,
    • 作为尚未编写的代码的占位符(就像pass声明)
    • 在切片语法中表示剩余方向上的完整切片

    >>> import numpy as np
    >>> three_dimensional_array = np.arange(8).reshape(2, 2, 2)
    array([
        [
            [0, 1],
            [2, 3]
        ],
    
        [
            [4, 5],
            [6, 7]
        ]
    ])

    所以我们的three_dimensional_array是由数组数组组成的数组。假设我们要打印第二个元素(index1)在所有最里面的数组中,我们可以使用省略号绕过前面的所有维度

    >>> three_dimensional_array[:,:,1]
    array([[1, 3],
       [5, 7]])
    >>> three_dimensional_array[..., 1] # using Ellipsis.
    array([[1, 3],
       [5, 7]])

    注意:这适用于任何数量的维度。您甚至可以选择第一个和最后一个维度中的切片,而忽略中间维度(n_dimensional_array[firs_dim_slice, ..., last_dim_slice])

    • 在……里面type hinting仅表示该类型的一部分(如(Callable[..., int]Tuple[str, ...]))
    • 您还可以使用省略号作为默认函数参数(在需要区分“没有传递参数”和“没有传递值”的情况下)

▶纯洁

这个拼写是有意的。请不要为此提交补丁

输出(Python 3.x):

>>> infinity = float('infinity')
>>> hash(infinity)
314159
>>> hash(float('-inf'))
-314159

💡说明:

  • 无穷大的散列是10⁵xπ
  • 有趣的是,float('-inf')在Python3中为“-10⁵xπ”,而在Python2中为“-10⁵x e

▶让我们毁了它吧

1个

class Yo(object):
    def __init__(self):
        self.__honey = True
        self.bro = True

输出:

>>> Yo().bro
True
>>> Yo().__honey
AttributeError: 'Yo' object has no attribute '__honey'
>>> Yo()._Yo__honey
True

2个

class Yo(object):
    def __init__(self):
        # Let's try something symmetrical this time
        self.__honey__ = True
        self.bro = True

输出:

>>> Yo().bro
True

>>> Yo()._Yo__honey__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Yo' object has no attribute '_Yo__honey__'

为什么要Yo()._Yo__honey工作?

3个

_A__variable = "Some value"

class A(object):
    def some_func(self):
        return __variable # not initialized anywhere yet

输出:

>>> A().__variable
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__variable'

>>> A().some_func()
'Some value'

💡说明:

  • Name Mangling用于避免不同命名空间之间的命名冲突。
  • 在Python中,解释器修改(损坏)以开头的类成员名称__(双下划线,也称为“下划线”),并且不能以多个尾部下划线结尾,方法是添加_NameOfTheClass在前面
  • 因此,要访问__honey属性,我们必须在第一个代码段中追加_Yo添加到前面,这样可以防止与任何其他类中定义的相同名称属性发生冲突
  • 但是为什么它在第二个片段中不起作用呢?因为名称损坏会排除以双下划线结尾的名称
  • 第三个代码片段也是名称损坏的结果。名字__variable在声明中return __variable被弄得残缺不全_A__variable,恰好也是我们在外部作用域中声明的变量的名称
  • 此外,如果损坏的名称超过255个字符,则会发生截断


部分:外表是有欺骗性的!

▶跳过台词?

输出:

>>> value = 11
>>> valuе = 32
>>> value
11

无精打采的?

注:要再现这一点,最简单的方法是简单地从上面的代码片段复制语句,并将它们粘贴到文件/shell中

💡解释

有些非西方字符看起来与英语字母表中的字母相同,但口译员认为它们是不同的

>>> ord('е') # cyrillic 'e' (Ye)
1077
>>> ord('e') # latin 'e', as used in English and typed using standard keyboard
101
>>> 'е' == 'e'
False

>>> value = 42 # latin e
>>> valuе = 23 # cyrillic 'e', Python 2.x interpreter would raise a `SyntaxError` here
>>> value
42

内置的ord()函数返回字符的Unicodecode point,并且西里尔文‘e’和拉丁文‘e’的不同代码位置证明了上述示例的行为


▶隐形传态

# `pip install numpy` first.
import numpy as np

def energy_send(x):
    # Initializing a numpy array
    np.array([float(x)])

def energy_receive():
    # Return an empty numpy array
    return np.empty((), dtype=np.float).tolist()

输出:

>>> energy_send(123.456)
>>> energy_receive()
123.456

诺贝尔奖在哪里?

💡说明:

  • 请注意,在energy_send函数不返回,因此内存空间可以自由重新分配。
  • numpy.empty()返回下一个可用内存插槽,而不重新初始化它。这个内存点恰好与刚刚释放的内存点相同(通常,但不总是)

▶嗯,有些事很可疑

def square(x):
    """
    A simple function to calculate the square of a number by addition.
    """
    sum_so_far = 0
    for counter in range(x):
        sum_so_far = sum_so_far + x
  return sum_so_far

输出(Python 2.x):

>>> square(10)
10

不是应该是100吗?

注:如果无法重现此文件,请尝试运行该文件mixed_tabs_and_spaces.py通过外壳

💡解释

  • 不要将制表符和空格混为一谈!紧接在回车之前的字符是“制表符”,并且在示例中的其他地方,代码以“4个空格”的倍数缩进
  • 以下是Python处理选项卡的方式:

    首先,制表符被替换(从左到右)1到8个空格,这样替换之前(包括替换)的字符总数是8的倍数<.>

  • 所以最后一行的“制表符”square函数被替换为8个空格,并进入循环
  • Python3非常友好,可以在这种情况下自动抛出错误

    输出(Python 3.x):

    TabError: inconsistent use of tabs and spaces in indentation


部分:其他

+=速度更快

# using "+", three strings:
>>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
0.25748300552368164
# using "+=", three strings:
>>> timeit.timeit("s1 += s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
0.012188911437988281

💡说明:

  • +=比我们的速度要快得多+用于连接两个以上的字符串,因为第一个字符串(例如,s1s1 += s2 + s3)在计算完整字符串时不会被销毁

▶让我们做一根巨大的绳子吧!

def add_string_with_plus(iters):
    s = ""
    for i in range(iters):
        s += "xyz"
    assert len(s) == 3*iters

def add_bytes_with_plus(iters):
    s = b""
    for i in range(iters):
        s += b"xyz"
    assert len(s) == 3*iters

def add_string_with_format(iters):
    fs = "{}"*iters
    s = fs.format(*(["xyz"]*iters))
    assert len(s) == 3*iters

def add_string_with_join(iters):
    l = []
    for i in range(iters):
        l.append("xyz")
    s = "".join(l)
    assert len(s) == 3*iters

def convert_list_to_string(l, iters):
    s = "".join(l)
    assert len(s) == 3*iters

输出:

# Executed in ipython shell using %timeit for better readability of results.
# You can also use the timeit module in normal python shell/scriptm=, example usage below
# timeit.timeit('add_string_with_plus(10000)', number=1000, globals=globals())

>>> NUM_ITERS = 1000
>>> %timeit -n1000 add_string_with_plus(NUM_ITERS)
124 µs ± 4.73 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS)
211 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_format(NUM_ITERS)
61 µs ± 2.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_join(NUM_ITERS)
117 µs ± 3.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> l = ["xyz"]*NUM_ITERS
>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS)
10.1 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

让我们将迭代次数增加10倍

>>> NUM_ITERS = 10000
>>> %timeit -n1000 add_string_with_plus(NUM_ITERS) # Linear increase in execution time
1.26 ms ± 76.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS) # Quadratic increase
6.82 ms ± 134 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_format(NUM_ITERS) # Linear increase
645 µs ± 24.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit -n1000 add_string_with_join(NUM_ITERS) # Linear increase
1.17 ms ± 7.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> l = ["xyz"]*NUM_ITERS
>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS) # Linear increase
86.3 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

💡解释

  • 您可以阅读更多关于timeit%timeit在这些链接上。它们用于度量代码片段的执行时间
  • 不要使用+为了生成长字符串-在Python中,str是不可变的,因此对于每对串联,必须将左字符串和右字符串复制到新字符串中。如果连接四个长度为10的字符串,您将复制(10+10)+((10+10)+10)+(10+10)+10)+10)=90个字符,而不仅仅是40个字符。随着字符串的数量和大小的增加,情况会变得平方恶化(这与add_bytes_with_plus功能)
  • 因此,建议您使用.format.%语法(但是,它们比+对于非常短的字符串)
  • 或者更好的是,如果您已经有了可迭代对象形式的内容,那么使用''.join(iterable_object)它的速度要快得多
  • 不像add_bytes_with_plus因为+=上一个示例中讨论的优化,add_string_with_plus没有表现出执行时间的二次增长。如果这份声明是s = s + "x" + "y" + "z"而不是s += "xyz",那么增长将是平方的。

    def add_string_with_plus(iters):
        s = ""
        for i in range(iters):
            s = s + "x" + "y" + "z"
        assert len(s) == 3*iters
    
    >>> %timeit -n100 add_string_with_plus(1000)
    388 µs ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    >>> %timeit -n100 add_string_with_plus(10000) # Quadratic increase in execution time
    9 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  • 因此,格式化和创建巨型字符串的许多方法与Zen of Python,根据它的说法,

    应该有1个,最好只有一个–显而易见的方法


▶减速dict查找*

some_dict = {str(i): 1 for i in range(1_000_000)}
another_dict = {str(i): 1 for i in range(1_000_000)}

输出:

>>> %timeit some_dict['5']
28.6 ns ± 0.115 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> some_dict[1] = 1
>>> %timeit some_dict['5']
37.2 ns ± 0.265 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

>>> %timeit another_dict['5']
28.5 ns ± 0.142 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> another_dict[1]  # Trying to access a key that doesn't exist
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 1
>>> %timeit another_dict['5']
38.5 ns ± 0.0913 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

为什么相同的查找速度变慢了?

💡说明:

  • CPython有一个通用字典查找函数,可以处理所有类型的键(strint,任何对象。),还有一个专门的,用于由以下内容组成的字典的常见情况str-仅密钥
  • 专用函数(名为lookdict_unicode在CPython的source)知道所有现有键(包括查找的键)都是字符串,并使用更快、更简单的字符串比较来比较键,而不是调用__eq__方法
  • 第一次dict实例使用非str键,则会对其进行修改,以便将来的查找使用泛型函数
  • 此过程对于特定情况是不可逆的dict实例,并且该键甚至不必存在于字典中。这就是为什么尝试失败的查找会产生同样的效果

▶膨胀的实例dict%s*

import sys

class SomeClass:
    def __init__(self):
        self.some_attr1 = 1
        self.some_attr2 = 2
        self.some_attr3 = 3
        self.some_attr4 = 4


def dict_size(o):
    return sys.getsizeof(o.__dict__)

输出:(Python 3.8,其他Python 3版本可能会稍有不同)

>>> o1 = SomeClass()
>>> o2 = SomeClass()
>>> dict_size(o1)
104
>>> dict_size(o2)
104
>>> del o1.some_attr1
>>> o3 = SomeClass()
>>> dict_size(o3)
232
>>> dict_size(o1)
232

我们再试一次。在新的口译器中:

>>> o1 = SomeClass()
>>> o2 = SomeClass()
>>> dict_size(o1)
104  # as expected
>>> o1.some_attr5 = 5
>>> o1.some_attr6 = 6
>>> dict_size(o1)
360
>>> dict_size(o2)
272
>>> o3 = SomeClass()
>>> dict_size(o3)
232

是什么让那些字典变得臃肿呢?为什么新创建的物体也会膨胀呢?

💡说明:

  • CPython能够在多个字典中重用相同的“键”对象。这是在PEP 412有减少内存使用的动机,特别是在实例字典中-其中键(实例属性)往往对所有实例都是通用的
  • 这种优化对于例如字典来说是完全无缝的,但是如果某些假设被打破,它将被禁用
  • 密钥共享字典不支持删除;如果实例属性被删除,则字典是“非共享”的,并且对同一类的所有未来实例禁用密钥共享
  • 此外,如果字典键已调整大小(因为插入了新键),则它们将保持共享仅限如果它们正好由单个字典使用(这允许在__init__第一个创建的实例的属性,而不会导致“取消共享”)。如果在调整大小时存在多个实例,则对同一类的所有未来实例禁用密钥共享:CPython无法知道您的实例是否再使用相同的属性集,因此决定放弃尝试共享它们的密钥
  • 如果您的目标是降低程序的内存占用量,那么给您一个小提示:不要删除实例属性,并确保初始化__init__好了!

▶次要的*

  • join()是字符串操作,而不是列表操作。(第一次使用时有点违反直觉)

    💡说明:如果join()是字符串上的方法,那么它可以操作任何可迭代的(列表、元组、迭代器)。如果它是列表上的方法,则必须由每种类型单独实现。此外,将特定于字符串的方法放在泛型list对象API

  • 一些看起来奇怪但语义正确的陈述:
    • [] = ()是语义上正确的语句(解包一个空的tuple变得空荡荡的list)
    • 'a'[0][0][0][0][0]与字符串一样,也是语义上正确的语句sequencesPython中的(支持使用整数索引访问元素的迭代数)
    • 3 --0-- 5 == 8--5 == 5都是语义上正确的语句,并且求值为True
  • 考虑到这一点a是一个数字,++a--a都是有效的Python语句,但行为方式与C、C++或Java等语言中的类似语句不同

    >>> a = 5
    >>> a
    5
    >>> ++a
    5
    >>> --a
    5

    💡说明:

    • 没有++Python语法中的运算符。实际上是两个+操作员
    • ++a解析为+(+a)这意味着a同样,语句的输出--a可以证明是合理的
    • 此堆栈溢出thread讨论Python中没有递增和递减运算符的原因
  • 您一定知道Python中的Walrus操作符。但是你有没有听说过太空入侵者操作员

    >>> a = 42
    >>> a -=- 1
    >>> a
    43

    它与另一个递增运算符一起用作另一个递增运算符

    >>> a +=+ 1
    >>> a
    >>> 44

    💡说明:这个恶作剧来自于Raymond Hettinger’s tweet空间入侵者操作符实际上只是一个格式错误的a -= (-1)这相当于a = a - (- 1)类似于a += (+ 1)案例

  • Python有一个未记录的converse implication操作员

    >>> False ** False == True
    True
    >>> False ** True == False
    True
    >>> True ** False == True
    True
    >>> True ** True == True
    True

    💡说明:如果你替换掉FalseTrue用0和1相乘并做数学运算,真值表等价于一个逆蕴涵运算符。(Source)

  • 既然我们说的是运营商,还有@矩阵乘法运算符(别担心,这次是实数)

    >>> import numpy as np
    >>> np.array([2, 2, 2]) @ np.array([7, 8, 8])
    46

    💡说明:这个@Python3.5中添加了运算符,将科学界考虑在内。任何对象都可以重载__matmul__定义此运算符行为的神奇方法

  • 从Python3.8开始,您可以使用典型的f-string语法,如下所示f'{some_var=}用于快速调试。例如,

    >>> some_string = "wtfpython"
    >>> f'{some_string=}'
    "some_string='wtfpython'"
  • Python使用2个字节存储函数中的局部变量。理论上,这意味着一个函数中只能定义65536个变量。但是,Python内置了一个方便的解决方案,可用于存储超过2^16个变量名。下面的代码演示了当定义了超过65536个局部变量时堆栈中会发生什么(警告:此代码打印大约2^18行文本,因此请做好准备!)

    import dis
    exec("""
    def f():
       """ + """
       """.join(["X" + str(x) + "=" + str(x) for x in range(65539)]))
    
    f()
    
    print(dis.dis(f))
  • 多个Python线程不会运行您的Python代码同时(是的,你没听错!)产生多个线程并让它们并发执行Python代码似乎很直观,但是由于Global Interpreter Lock在Python中,您所要做的就是让您的线程轮流在同一内核上执行。Python线程适用于IO受限的任务,但要在Python中实现CPU受限任务的实际并行化,您可能需要使用Pythonmultiprocessing模块
  • 有时候,print方法可能不会立即打印值。例如,

    # File some_file.py
    import time
    
    print("wtfpython", end="_")
    time.sleep(3)

    这将打印wtfpython3秒后,由于end参数,因为输出缓冲区在遇到\n或者当程序完成执行时。我们可以通过传递以下参数来强制刷新缓冲区flush=True论据

  • 索引超出界限的列表切片不会引发错误

    >>> some_list = [1, 2, 3, 4, 5]
    >>> some_list[111:]
    []
  • 对迭代数进行切片并不总是会创建一个新对象。例如,

    >>> some_str = "wtfpython"
    >>> some_list = ['w', 't', 'f', 'p', 'y', 't', 'h', 'o', 'n']
    >>> some_list is some_list[:] # False expected because a new object is created.
    False
    >>> some_str is some_str[:] # True because strings are immutable, so making a new object is of not much use.
    True
  • int('١٢٣٤٥٦٧٨٩')退货123456789在Python 3中。在Python中,十进制字符包括数字字符和所有可用于构成小数基数的字符,例如U+0660,阿拉伯数字0。这是一张interesting story与Python的此行为相关
  • 从Python3开始,可以使用下划线分隔数字文字(以提高可读性

    >>> six_million = 6_000_000
    >>> six_million
    6000000
    >>> hex_address = 0xF00D_CAFE
    >>> hex_address
    4027435774
  • 'abc'.count('') == 4以下是以下内容的大致实现count方法,这将使事情变得更清楚。

    def count(s, sub):
        result = 0
        for i in range(len(s) + 1 - len(sub)):
            result += (s[i:i + len(sub)] == sub)
        return result

    该行为是由于匹配空的子字符串(''),并在原始字符串中包含长度为0的片段



贡献

您可以通过几种方式为wtfpython做贡献,

  • 提出新的例子
  • 帮助翻译(请参见issues labeled translation)
  • 较小的更正,如指出过期的代码片段、打字错误、格式错误等
  • 找出差距(如解释不充分、重复示例等)
  • 有没有让这个项目更有趣、更有用的创造性建议?

请看CONTRIBUTING.md了解更多详细信息。您可以随意创建新的issue讨论事情

PS:请不要联系反向链接请求,不会添加任何链接,除非它们与项目高度相关

确认

这个系列的想法和设计最初的灵感来自Denys Dovhan令人惊叹的项目wtfjsPythonistas的压倒性支持给了它现在的样子

一些不错的链接!

🎓许可证

WTFPL 2.0

©Satwik Kansal

让你的朋友也大吃一惊吧!

如果您喜欢wtfpython,您可以使用这些快速链接与您的朋友分享它,

Twitter|Linkedin|Facebook

需要pdf版本吗?

我收到了一些关于wtfpython的pdf(和epub)版本的请求。您可以添加您的详细信息here一做完就拿到

这就是所有的人!对于即将发布的此类内容,您可以添加您的电子邮件here

MadeWithML 了解如何通过ML负责任地交付产品

基础知识

通过直观的解释、干净的代码和可视化学习ML的基础

🔢三个基金会 📈数据建模 🤖*深度学习
Notebooks Linear Regression CNNs
Python Logistic Regression Embeddings
NumPy Neural Network RNNs
Pandas Data Quality Transformers
PyTorch Utilities

📆更多话题即将到来!
Subscribe查看我们每月更新的新内容

MLOPS

了解如何应用ML来构建生产级产品以交付价值

📦新产品 📝使用脚本编写 ♻️*可重现性
Objective Organization Git
Solution Packaging Pre-commit
Iteration Documentation Versioning
🔢三个数据 Styling Docker
Labeling Makefile 🚀三个产品的生产
Preprocessing Logging Dashboard
Exploratory data analysis 📦3个接口 CI/CD workflows
Splitting Command-line Infrastructure
Augmentation RESTful API Monitoring
📈数据建模 ✅测试结果: Pipelines
Evaluation Code Feature store
Experiment tracking Data
Optimization Models

📆每个月都有新的课程!
Subscribe查看我们每月更新的新内容

常见问题解答

这个内容是为谁准备的?

  • Software engineers希望学习ML并成为更好的软件工程师
  • Data scientists谁想了解如何通过ML负责任地交付价值
  • College graduates希望学习该行业所需的实用技能
  • Product Managers谁想为ML应用程序开发技术基础

它的结构是什么?

课程将每周发布一次,每节课程包括:

  • intuition:将涵盖的概念及其如何组合在一起的高级概述
  • code:说明概念的简单代码示例
  • application:将概念应用于我们的具体任务
  • extensions:简要介绍适用于不同情况的其他工具和技术

是什么让这个内容独一无二的呢?

  • hands-on:如果您在线搜索Production ML或MLOps,您会找到很棒的博客帖子和tweet。但是为了真正理解这些概念,您需要实现它们。不幸的是,由于规模、专有内容和昂贵的工具,您没有看到很多运行Production ML的内部工作原理。然而,Made with ML是免费的、开放的和活生生的,这使得它成为社区完美的学习机会
  • intuition-first:我们永远不会直接跳到代码上去。在每节课中,我们都会培养对概念的直觉,并从产品的角度来思考。
  • software engineering:本课程不只是关于ML。事实上,它主要是关于干净的软件工程!我们将介绍一些重要的概念,如版本控制、测试、日志记录等,它们可以真正成为生产级产品
  • focused yet holistic:对于每个概念,我们不仅会介绍对我们的特定任务最重要的内容(这是案例研究方面),而且还会介绍相关的方法(这是指导方面),这些方法在其他情况下可能会被证明是有用的

作者是谁?

  • 我在苹果公司部署了大规模ML系统,也在初创公司部署了受约束的较小系统,我想与大家分享我学到的共同原则
  • 与我保持联系TwitterLinkedIn

为什么这个是免费的?

虽然这个内容是为每个人准备的,但它特别针对那些没有太多学习机会的人。我相信创造力和智慧是随机分布的,而机会是孤立的。我想让更多的人创造并为创新做出贡献


要引用此内容,请使用:

@misc{madewithml,
    title  = "Made With ML",
    author = "Goku Mohandas",
    url    = "https://madewithml.com/"
    year   = "2021",
}

Real-Time-Voice-Cloning 5秒内克隆语音,实时生成任意语音

实时语音克隆

此存储库是Transfer Learning from Speaker Verification to
Multispeaker Text-To-Speech Synthesis
(SV2TTS),具有实时工作的声码器。请随时查看my thesis如果你很好奇,或者你在找我没有记录的信息。大多数情况下,我建议快速浏览一下导言之外的数字。

SV2TTS是一个三阶段深度学习框架,它允许从几秒钟的音频创建语音的数字表示,并使用它来调整文本到语音的模型,该模型经过训练以概括为新的语音

视频演示(点击图片):

Toolbox demo

已实施的文件

URL 指定 标题 实施来源
1806.04558 SV2TTS 从说话人确认到多说话人文语合成的转移学习 此回购
1802.08435 WaveRNN(声码器) 高效的神经音频合成 fatchord/WaveRNN
1703.10135 泰科加速器(合成器) Taco tron:走向端到端语音合成 fatchord/WaveRNN
1710.10467 GE2E(编码器) 说话人确认的广义端到端损耗 此回购

新闻

14/02/21:这个回购现在运行在PyTorch上,而不是TensorFlow上,这要归功于@Bluefish的帮助。如果希望改为运行TensorFlow版本,请签出提交5425557

13/11/19:我现在是全职工作,我不会再维持这个回购了。致任何阅读此文的人:

  • 如果你只想克隆你的声音(而不是别人的):我推荐我们的免费计划Resemble.AI你会得到更好的音质和更少的韵律错误
  • 如果这不是您的情况:继续使用此存储库,但您可能最终会对结果感到失望。如果你计划做一个严肃的项目,我的强烈建议是:另找一个TTS回收站。去here了解更多信息

20/08/19:我正在努力resemblyzer,一个独立的语音编码器软件包。您可以使用此回收站中经过训练的编码器型号与其配合使用

06/07/19:需要在远程服务器上的坞站容器中运行吗?看见here

25/06/19:为合成器增加了对低内存GPU(~2 GB)的实验支持。经过--low_memdemo_cli.pydemo_toolbox.py来启用它。这会增加很大的开销,因此如果您有足够的VRAM,则不建议使用

设置

1.安装要求

Python 3.6或3.7运行工具箱所需的

  • 安装PyTorch(>=1.0.1)
  • 安装ffmpeg
  • pip install -r requirements.txt要安装剩余的必要软件包,请执行以下操作

2.下载预先训练好的模型

下载最新版本here

3.(可选)测试配置

在下载任何数据集之前,您可以通过以下方式开始测试配置:

python demo_cli.py

如果所有测试都通过了,你就可以走了

4.(可选)下载数据集

对于单独使用工具箱,我只推荐下载LibriSpeech/train-clean-100将内容提取为<datasets_root>/LibriSpeech/train-clean-100哪里<datasets_root>是您选择的目录。工具箱中支持其他数据集,请参阅here您可以不下载任何数据集,但是您需要将您自己的数据作为音频文件,否则您必须使用工具箱进行记录

5.启动工具箱

然后,您可以尝试该工具箱:

python demo_toolbox.py -d <datasets_root>

python demo_toolbox.py

取决于您是否下载了任何数据集。如果您正在运行X服务器或出现错误Aborted (core dumped),请参见this issue