pyupgrade
1.0.0
一个工具(和预提交挂钩),用于自动升级该语言新版本的语法。
pip install pyupgrade
请参阅预提交以获取说明
示例.pre-commit-config.yaml
:
- repo : https://github.com/asottile/pyupgrade
rev : v3.19.0
hooks :
- id : pyupgrade
- set(())
+ set()
- set([])
+ set()
- set((1,))
+ {1}
- set((1, 2))
+ {1, 2}
- set([1, 2])
+ {1, 2}
- set(x for x in y)
+ {x for x in y}
- set([x for x in y])
+ {x for x in y}
- dict((a, b) for a, b in y)
+ {a: b for a, b in y}
- dict([(a, b) for a, b in y])
+ {a: b for a, b in y}
collections.defaultdict
调用中不必要的 lambda - defaultdict(lambda: [])
+ defaultdict(list)
- defaultdict(lambda: list())
+ defaultdict(list)
- defaultdict(lambda: {})
+ defaultdict(dict)
- defaultdict(lambda: dict())
+ defaultdict(dict)
- defaultdict(lambda: ())
+ defaultdict(tuple)
- defaultdict(lambda: tuple())
+ defaultdict(tuple)
- defaultdict(lambda: set())
+ defaultdict(set)
- defaultdict(lambda: 0)
+ defaultdict(int)
- defaultdict(lambda: 0.0)
+ defaultdict(float)
- defaultdict(lambda: 0j)
+ defaultdict(complex)
- defaultdict(lambda: '')
+ defaultdict(str)
- '{0} {1}'.format(1, 2)
+ '{} {}'.format(1, 2)
- '{0}' '{1}'.format(1, 2)
+ '{}' '{}'.format(1, 2)
可用性:
--keep-percent-format
。 - '%s %s' % (a, b)
+ '{} {}'.format(a, b)
- '%r %2f' % (a, b)
+ '{!r} {:2f}'.format(a, b)
- '%(a)s %(b)s' % {'a': 1, 'b': 2}
+ '{a} {b}'.format(a=1, b=2)
- u'foo'
+ 'foo'
- u"foo"
+ 'foo'
- u'''foo'''
+ '''foo'''
# strings with only invalid sequences become raw strings
- 'd'
+ r'd'
# strings with mixed valid / invalid sequences get escaped
- 'nd'
+ 'n\d'
- u'd'
+ r'd'
# this fixes a syntax error in python3.3+
- 'N'
+ r'N'
is
/ is not
与常量文字比较在 python3.8+ 中,与文字的比较成为SyntaxWarning
因为这些比较的成功与否是特定于实现的(由于公共对象缓存)。
- x is 5
+ x == 5
- x is not 5
+ x != 5
- x is 'foo'
+ x == 'foo'
.encode()
到字节文字 - 'foo'.encode()
+ b'foo'
- 'foo'.encode('ascii')
+ b'foo'
- 'foo'.encode('utf-8')
+ b'foo'
- u'foo'.encode()
+ b'foo'
- 'xa0'.encode('latin1')
+ b'xa0'
print(...)
中的无关括号python-modernize/python-modernize#178 的修复
# ok: printing an empty tuple
print(())
# ok: printing a tuple
print((1,))
# ok: parenthesized generator argument
sum((i for i in range(3)), [])
# fixed:
- print(("foo"))
+ print("foo")
isinstance
/ issubclass
/ except
- isinstance(x, (int, int))
+ isinstance(x, int)
- issubclass(y, (str, str))
+ issubclass(y, str)
try:
raises()
- except (Error1, Error1, Error2):
+ except (Error1, Error2):
pass
将已弃用的单元测试方法别名重写为其未弃用的形式。
from unittest import TestCase
class MyTests(TestCase):
def test_something(self):
- self.failUnlessEqual(1, 1)
+ self.assertEqual(1, 1)
- self.assertEquals(1, 1)
+ self.assertEqual(1, 1)
super()
调用 class C(Base):
def f(self):
- super(C, self).f()
+ super().f()
- class C(object): pass
+ class C: pass
- class C(B, object): pass
+ class C(B): pass
__metaclass__ = type
声明 class C:
- __metaclass__ = type
str("native")
文字 - str()
+ ''
- str("foo")
+ "foo"
.encode("utf-8")
- "foo".encode("utf-8")
+ "foo".encode()
# coding: ...
评论从 PEP 3120 开始,python 源的默认编码是 UTF-8
- # coding: utf-8
x = 1
__future__
导入移除可用性:
nested_scopes
, generators
, with_statement
, absolute_import
, division
, print_function
, unicode_literals
--py37-plus
还将删除generator_stop
- from __future__ import with_statement
- from io import open
- from six.moves import map
- from builtins import object # python-future
可用性:
--py36-plus
(和其他)将取代导入另请参阅 reorder-python-imports
一些例子:
- from collections import deque, Mapping
+ from collections import deque
+ from collections.abc import Mapping
- from typing import Sequence
+ from collections.abc import Sequence
- from typing_extensions import Concatenate
+ from typing import Concatenate
mock
导入可用性:
--keep-mock
。 - from mock import patch
+ from unittest.mock import patch
yield
=> yield from
def f():
- for x in y:
- yield x
+ yield from y
- for a, b in c:
- yield (a, b)
+ yield from c
import sys
- if sys.version_info < (3,): # also understands `six.PY2` (and `not`), `six.PY3` (and `not`)
- print('py2')
- else:
- print('py3')
+ print('py3')
可用性:
--py36-plus
将删除 Python <= 3.5 only 块--py37-plus
将删除仅 Python <= 3.6 的块 # using --py36-plus for this example
import sys
- if sys.version_info < (3, 6):
- print('py3.5')
- else:
- print('py3.6+')
+ print('py3.6+')
- if sys.version_info <= (3, 5):
- print('py3.5')
- else:
- print('py3.6+')
+ print('py3.6+')
- if sys.version_info >= (3, 6):
- print('py3.6+')
- else:
- print('py3.5')
+ print('py3.6+')
请注意,没有else
if
块不会被重写,因为它可能会引入语法错误。
six
兼容性代码 - six.text_type
+ str
- six.binary_type
+ bytes
- six.class_types
+ (type,)
- six.string_types
+ (str,)
- six.integer_types
+ (int,)
- six.unichr
+ chr
- six.iterbytes
+ iter
- six.print_(...)
+ print(...)
- six.exec_(c, g, l)
+ exec(c, g, l)
- six.advance_iterator(it)
+ next(it)
- six.next(it)
+ next(it)
- six.callable(x)
+ callable(x)
- six.moves.range(x)
+ range(x)
- six.moves.xrange(x)
+ range(x)
- from six import text_type
- text_type
+ str
- @six.python_2_unicode_compatible
class C:
def __str__(self):
return u'C()'
- class C(six.Iterator): pass
+ class C: pass
- class C(six.with_metaclass(M, B)): pass
+ class C(B, metaclass=M): pass
- @six.add_metaclass(M)
- class C(B): pass
+ class C(B, metaclass=M): pass
- isinstance(..., six.class_types)
+ isinstance(..., type)
- issubclass(..., six.integer_types)
+ issubclass(..., int)
- isinstance(..., six.string_types)
+ isinstance(..., str)
- six.b('...')
+ b'...'
- six.u('...')
+ '...'
- six.byte2int(bs)
+ bs[0]
- six.indexbytes(bs, i)
+ bs[i]
- six.int2byte(i)
+ bytes((i,))
- six.iteritems(dct)
+ dct.items()
- six.iterkeys(dct)
+ dct.keys()
- six.itervalues(dct)
+ dct.values()
- next(six.iteritems(dct))
+ next(iter(dct.items()))
- next(six.iterkeys(dct))
+ next(iter(dct.keys()))
- next(six.itervalues(dct))
+ next(iter(dct.values()))
- six.viewitems(dct)
+ dct.items()
- six.viewkeys(dct)
+ dct.keys()
- six.viewvalues(dct)
+ dct.values()
- six.create_unbound_method(fn, cls)
+ fn
- six.get_unbound_function(meth)
+ meth
- six.get_method_function(meth)
+ meth.__func__
- six.get_method_self(meth)
+ meth.__self__
- six.get_function_closure(fn)
+ fn.__closure__
- six.get_function_code(fn)
+ fn.__code__
- six.get_function_defaults(fn)
+ fn.__defaults__
- six.get_function_globals(fn)
+ fn.__globals__
- six.raise_from(exc, exc_from)
+ raise exc from exc_from
- six.reraise(tp, exc, tb)
+ raise exc.with_traceback(tb)
- six.reraise(*sys.exc_info())
+ raise
- six.assertCountEqual(self, a1, a2)
+ self.assertCountEqual(a1, a2)
- six.assertRaisesRegex(self, e, r, fn)
+ self.assertRaisesRegex(e, r, fn)
- six.assertRegex(self, s, r)
+ self.assertRegex(s, r)
# note: only for *literals*
- six.ensure_binary('...')
+ b'...'
- six.ensure_str('...')
+ '...'
- six.ensure_text('...')
+ '...'
open
别名 - with io.open('f.txt') as f:
+ with open('f.txt') as f:
...
open
模式 - open("foo", "U")
+ open("foo")
- open("foo", "Ur")
+ open("foo")
- open("foo", "Ub")
+ open("foo", "rb")
- open("foo", "rUb")
+ open("foo", "rb")
- open("foo", "r")
+ open("foo")
- open("foo", "rt")
+ open("foo")
- open("f", "r", encoding="UTF-8")
+ open("f", encoding="UTF-8")
- open("f", "wt")
+ open("f", "w")
OSError
错误别名 # also understands:
# - IOError
# - WindowsError
# - mmap.error and uses of `from mmap import error`
# - select.error and uses of `from select import error`
# - socket.error and uses of `from socket import error`
def throw():
- raise EnvironmentError('boom')
+ raise OSError('boom')
def catch():
try:
throw()
- except EnvironmentError:
+ except OSError:
handle_error()
TimeoutError
别名可用性:
--py310-plus
用于socket.timeout
--py311-plus
用于asyncio.TimeoutError
def throw(a):
if a:
- raise asyncio.TimeoutError('boom')
+ raise TimeoutError('boom')
else:
- raise socket.timeout('boom')
+ raise TimeoutError('boom')
def catch(a):
try:
throw(a)
- except (asyncio.TimeoutError, socket.timeout):
+ except TimeoutError:
handle_error()
typing.Text
str 别名 - def f(x: Text) -> None:
+ def f(x: str) -> None:
...
- foo, bar, baz = [fn(x) for x in items]
+ foo, bar, baz = (fn(x) for x in items)
xml.etree.cElementTree
重写为xml.etree.ElementTree
- import xml.etree.cElementTree as ET
+ import xml.etree.ElementTree as ET
- from xml.etree.cElementTree import XML
+ from xml.etree.ElementTree import XML
type
- type('')
+ str
- type(b'')
+ bytes
- type(0)
+ int
- type(0.)
+ float
typing.NamedTuple
/ typing.TypedDict
py36+ 语法可用性:
--py36-plus
在命令行上传递。 - NT = typing.NamedTuple('NT', [('a', int), ('b', Tuple[str, ...])])
+ class NT(typing.NamedTuple):
+ a: int
+ b: Tuple[str, ...]
- D1 = typing.TypedDict('D1', a=int, b=str)
+ class D1(typing.TypedDict):
+ a: int
+ b: str
- D2 = typing.TypedDict('D2', {'a': int, 'b': str})
+ class D2(typing.TypedDict):
+ a: int
+ b: str
可用性:
--py36-plus
在命令行上传递。 - '{foo} {bar}'.format(foo=foo, bar=bar)
+ f'{foo} {bar}'
- '{} {}'.format(foo, bar)
+ f'{foo} {bar}'
- '{} {}'.format(foo.bar, baz.womp)
+ f'{foo.bar} {baz.womp}'
- '{} {}'.format(f(), g())
+ f'{f()} {g()}'
- '{x}'.format(**locals())
+ f'{x}'
注意: pyupgrade
故意胆怯,如果它会使表达式更长或者替换参数足够复杂(因为这会降低可读性),则不会创建 f 字符串。
subprocess.run
:用text
替换universal_newlines
可用性:
--py37-plus
在命令行上传递。 - output = subprocess.run(['foo'], universal_newlines=True)
+ output = subprocess.run(['foo'], text=True)
subprocess.run
:用capture_output=True
替换stdout=subprocess.PIPE, stderr=subprocess.PIPE
可用性:
--py37-plus
在命令行上传递。 - output = subprocess.run(['foo'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = subprocess.run(['foo'], capture_output=True)
@functools.lru_cache()
中删除括号可用性:
--py38-plus
在命令行上传递。 import functools
- @functools.lru_cache()
+ @functools.lru_cache
def expensive():
...
可用性:
--py38-plus
在命令行上传递。 - ' '.join(shlex.quote(arg) for arg in cmd)
+ shlex.join(cmd)
@functools.lru_cache(maxsize=None)
替换为简写可用性:
--py39-plus
在命令行上传递。 import functools
- @functools.lru_cache(maxsize=None)
+ @functools.cache
def expensive():
...
可用性:
from __future__ import annotations
导入文件--keep-runtime-typing
。--py39-plus
在命令行上传递。 - def f(x: List[str]) -> None:
+ def f(x: list[str]) -> None:
...
可用性:
from __future__ import annotations
导入文件--keep-runtime-typing
。--py310-plus
在命令行上传递。 - def f() -> Optional[str]:
+ def f() -> str | None:
...
- def f() -> Union[int, str]:
+ def f() -> int | str:
...
可用性:
from __future__ import annotations
导入文件--keep-runtime-typing
。--py313-plus
在命令行上传递。 - def f() -> Generator[int, None, None]:
+ def f() -> Generator[int]:
yield 1
- async def f() -> AsyncGenerator[int, None]:
+ async def f() -> AsyncGenerator[int]:
yield 1
可用性:
from __future__ import annotations
导入文件 - def f(x: 'queue.Queue[int]') -> C:
+ def f(x: queue.Queue[int]) -> C:
datetime.UTC
别名可用性:
--py311-plus
在命令行上传递。 import datetime
- datetime.timezone.utc
+ datetime.UTC