PEP 3115 -- Metaclasses in Python 3000,元类
元类就是深度的魔法,99%的用户应该根本不必为此操心。
如果你想搞清楚 究竟是否需要用到元类,那么你就不需要它。
那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。
------ TimPeters
这玩意我看了很久才看明白,因为Java和C++里都没有类似的概念。
推荐这篇文章Python黑魔法:元类和元编程 - 优刻得云计算的文章 - 知乎 写得真不错
概念
通俗来说,元类是控制类的创建的类 。
通过继承 type
可以定义自定义元类,核心是重写 __new__
方法(负责创建类)或 __init__
方法(负责初始化类)。
自定义元类的基本语法如下:
python
class MetaClass(type):
def __new__(cls, name, bases, attrs):
print(f"Creating class: {name}")
return type.__new__(cls, name, bases, attrs)
def __init__(cls, name, bases, attrs):
print(f"Initializing class: {name}")
type.__init__(cls, name, bases, attrs)
class ClassA(object, metaclass=MetaClass):
pass
元类不是父类 。上面的代码可能会让你误认为ClassA
的两个方法继承了MetaClass
的,由于ClassA
没有实例化,运行应该不会有任何打印。但实际上两行print
都会打印。
bash
Creating class: ClassA
Initializing class: ClassA
元类的两个方法是在创建类时调用的。逻辑比较相似。一般来说,如果操作需要影响类的 "创建结果"(如修改类名、基类、决定是否创建),应该放在 __new__
;如果操作仅需 "基于已创建的类进行配置",优先放在 __init__
。概念是这样,但是具体还要看应用场景。
一个典型的元类的应用场景就是:单例模式
python
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
print("Connecting to database...")
# 测试
db1 = Database()
db2 = Database()
print(db1 is db2) # True
如上代码可以确保无论全局初始化多少个Database
实例,实际上都指向同一个实例。
关于元类的更多内容,我会专门写篇文章介绍,这里就不说了。
PEP 3120 -- Using UTF-8 as the default source encoding,默认 UTF-8
在 Python 2 中,源代码文件的默认编码是 ASCII。这意味着:
python
# Python 2 中,以下代码会出错(除非显式声明编码)
name = "你好"
print name
因为 "你好" 不是 ASCII 字符,Python 2 默认无法解析,会抛出:
bash
SyntaxError: Non-ASCII character ...
为了解决这个问题,Python 2 引入了 PEP 263,允许在文件顶部用注释指定编码。比如utf-8
就可以如此声明:
bash
# -*- coding: utf-8 -*-
name = "你好"
print name
但是老这么写也不是个办法。既然大家写utf-8
的最多,不如就改成默认这个吧。这就是PEP 3120所做的,Python 3 将源代码文件的默认编码从 ASCII 改为 UTF-8
- 所有 .py 文件默认以 UTF-8 编码读取。
- 无需再写
# -*- coding: utf-8 -*-
(除非你用的是其他编码,如 GBK、Latin-1 等)。 - 可以直接在字符串、变量名(受限)、注释中使用中文、emoji、希腊字母等 Unicode 字符。
原有的编码声明得以保留,方便从utf-8
切换其他编码。
PEP 3333 -- Python Web Server Gateway Interface v1.0.1,Web 开发
今天如果用Python编写一个web应用,有各种各样的框架可以使用。而WSGI就是这些框架的基本接口。
当用户在浏览器访问 http://example.com/hello 时:
bash
用户浏览器
↓ (HTTP 请求)
Web 服务器(Gunicorn / uWSGI)
↓ (调用 WSGI 应用)
Web 框架(Flask / Django)
↓ (生成响应)
Web 服务器
↓ (返回 HTTP 响应)
用户浏览器
服务器和框架之间,需要有某种接口进行协调,这就是WSGI(不考虑异步的场景,这里只说同步)只要框架返回一个符合WSGI 的 application,任何 WSGI 服务器(Gunicorn、uWSGI、mod_wsgi)都能运行它。
写一个简单的 app:
python
from wsgiref.simple_server import make_server
def app(environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/plain; charset=utf-8')]
start_response(status, headers)
return [b'Hello from WSGI server!']
# 创建服务器,监听 8000 端口
with make_server('', 8000, app) as httpd:
print("Serving on port 8000...")
httpd.serve_forever()
运行以后在终端执行如下命令:
bash
curl http://localhost:8000
收到服务器的响应:
bash
Hello from WSGI server!
同时服务端也有打印:
bash
127.0.0.1 - - [16/Aug/2025 14:54:45] "GET / HTTP/1.1" 200 23
这个 app 就是一个符合 WSGI 协议的 web 应用,同样的,我们不自己实现,而是使用一些 web 框架编写 app,也可以交给 wsgiref 运行,此时 wsgiref 就是服务器。我们以 flask 为例,仅仅修改 app 侧的代码,服务器侧完全不需要修改:
python
from flask import Flask
from wsgiref.simple_server import make_server
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Flask!'
with make_server('', 8000, app.wsgi_app) as httpd:
httpd.serve_forever()
flask的app.run
,本质上也是使用wsgiref
,因此会弹出类似警告:
python
WARNING: This is a development server. Do not use it in a production setting.
建议还是用现代的异步框架,比如uvicorn(服务器)+ fastapi(web应用)的组合