GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing python-future.org — What else you need to know — Python-Future documentation What else you need to know¶The following points are important to know about when writing Python 2/3 compatible code. bytes¶Handling
>>> from builtins import bytes >>> b = bytes(b'ABCD') On Py3, this is simply the builtin >>> b + u'EFGH' # TypeError Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: argument can't be unicode string >>> bytes(b',').join([u'Fred', u'Bill']) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sequence item 0: expected bytes, found unicode string >>> b == u'ABCD' False >>> b < u'abc' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: bytes() and <type 'unicode'> In most other ways, these b = bytes(b'ABCD') assert list(b) == [65, 66, 67, 68] assert repr(b) == "b'ABCD'" assert b.split(b'B') == [b'A', b'CD'] Currently the easiest way to ensure identical behaviour of byte-strings
in a Py2/3 codebase is to wrap all byte-string literals from builtins import bytes # ... b = bytes(b'This is my bytestring') # ... This is not perfect, but it is superior to manually debugging and fixing code incompatibilities caused by the many differences between Py3 bytes and Py2 strings. The >>> from builtins import bytes >>> b = bytes(b'\xff') >>> b.decode('utf-8', 'surrogateescape') '\udcc3' This feature is in alpha. Please leave feedback here about whether this works for you. str¶The The major difference is the stricter type-checking of Py3’s There are also other differences, such as the
>>> from __future__ import unicode_literals >>> from builtins import str On Py2, this gives us: >>> str future.types.newstr.newstr (On Py3, it is simply the usual builtin Then, for example, the following code has the same effect on Py2 as on Py3: >>> s = str(u'ABCD') >>> assert s != b'ABCD' >>> assert isinstance(s.encode('utf-8'), bytes) >>> assert isinstance(b.decode('utf-8'), str) These raise TypeErrors: >>> bytes(b'B') in s Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'in <string>' requires string as left operand, not <type 'str'> >>> s.find(bytes(b'A')) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: argument can't be <type 'str'> Various other operations that mix strings and bytes or other types are
permitted on Py2 with the >>> s2 = b'/' + str('ABCD') >>> s2 '/ABCD' >>> type(s2) future.types.newstr.newstr This is allowed for compatibility with parts of the Python 2 standard
library and various third-party libraries that mix byte-strings and unicode
strings loosely. One example is In most other ways, these >>> s = str('ABCD') >>> assert repr(s) == 'ABCD' # consistent repr with Py3 (no u prefix) >>> assert list(s) == ['A', 'B', 'C', 'D'] >>> assert s.split('B') == ['A', 'CD'] The >>> from builtins import str >>> s = str(u'\udcff') >>> s.encode('utf-8', 'surrogateescape') b'\xff' This feature is in alpha. Please leave feedback here about whether this works for you. dict¶Python 3 dictionaries have If your dictionaries are small, performance is not critical, and you don’t need the set-like behaviour of iterator objects from Python 3, you can of course stick with standard Python 3 code in your Py2/3 compatible codebase: # Assuming d is a native dict ... for key in d: # code here for item in d.items(): # code here for value in d.values(): # code here In this case there will be memory overhead of list creation on Py2 for each
call to For improved efficiency, from __future__ import print_function from builtins import dict, range # Memory-efficient construction: d = dict((i, i**2) for i in range(10**7)) assert not isinstance(d.items(), list) # Because items() is memory-efficient, so is this: d2 = dict((v, k) for (k, v) in d.items()) As usual, on Python 3 Memory-efficiency and alternatives¶If you already have large native dictionaries, the downside to wrapping them in
a # This allocates and then frees a large amount of temporary memory: d = dict({i: i**2 for i in range(10**7)}) If dictionary methods like The memory-efficient (and CPU-efficient) alternatives are:
For compatibility, the functions int¶Python 3’s Python 3: >>> 2**64 18446744073709551616 Python 2: >>> 2**64 18446744073709551616L
>>> from builtins import int >>> must_be_a_long_integer = int(1234) The backported from builtins import int def longify(data): """ Turns data (an int or long, or a list of ints or longs) into a list of longs. """ if not data: return [int(0)] if not isinstance(data, list): return [int(data)] return list(map(int, data)) Without def longify(data): """ Turns data (an int or long, or a list of ints or longs) into a list of longs. """ if not data: if PY3: return [0] else: return [long(0)] if not isinstance(data,list): if PY3: return [int(data)] else: return [long(data)] if PY3: return list(map(int, data)) # same as returning data, but with up-front typechecking else: return list(map(long, data)) isinstance¶The following tests all pass on Python 3: >>> assert isinstance(2**62, int) >>> assert isinstance(2**63, int) >>> assert isinstance(b'my byte-string', bytes) >>> assert isinstance(u'unicode string 1', str) >>> assert isinstance('unicode string 2', str) However, two of these normally fail on Python 2: >>> assert isinstance(2**63, int) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError >>> assert isinstance(u'my unicode string', str) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError And if this import is in effect on Python 2: >>> from __future__ import unicode_literals then the fifth test fails too: >>> assert isinstance('unicode string 2', str) Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError After importing the builtins from >>> from builtins import bytes, int, str >>> assert isinstance(10, int) >>> assert isinstance(10**100, int) >>> assert isinstance(b'my byte-string', bytes) >>> assert isinstance(u'unicode string 1', str) However, note that the last test requires that >>> from __future__ import unicode_literals >>> assert isinstance('unicode string 2', str) This works because the backported types Passing data to/from Python 2 libraries¶If you are passing any of the backported types ( The >>> from builtins import int, bytes, str >>> from future.utils import native >>> a = int(10**20) # Py3-like long int >>> a 100000000000000000000 >>> type(a) future.types.newint.newint >>> native(a) 100000000000000000000L >>> type(native(a)) long >>> b = bytes(b'ABC') >>> type(b) future.types.newbytes.newbytes >>> native(b) 'ABC' >>> type(native(b)) str >>> s = str(u'ABC') >>> type(s) future.types.newstr.newstr >>> native(s) u'ABC' >>> type(native(s)) unicode On Py3, the Native string type¶Some library code, include standard library code like the The objects The functions open()¶The Python 3 builtin open(filename, 'rb') in which case its methods like On Py2 with One difference to be aware of between the Python 3 from __future__ import unicode_literals from builtins import open, bytes data = open('image.png', 'rb').read() # On Py2, data is a standard 8-bit str with loose Unicode coercion. # data + u'' would likely raise a UnicodeDecodeError data = bytes(data) # Now it behaves like a Py3 bytes object... assert data[:4] == b'\x89PNG' assert data[4] == 13 # integer # Raises TypeError: # data + u'' Custom __str__ methods¶If you define a custom Use the following decorator to map the from future.utils import python_2_unicode_compatible @python_2_unicode_compatible class MyClass(object): def __str__(self): return u'Unicode string: \u5b54\u5b50' a = MyClass() # This then prints the name of a Chinese philosopher: print(a) This decorator is identical to the decorator of the same name in
This decorator is a no-op on Python 3. Custom iterators¶If you define your own iterators, there is an incompatibility in the method name
to retrieve the next item across Py3 and Py2. On Python 3 it is The most elegant solution to this is to derive your custom iterator class from
from builtins import object class Upper(object): def __init__(self, iterable): self._iter = iter(iterable) def __next__(self): # Py3-style iterator interface return next(self._iter).upper() def __iter__(self): return self itr = Upper('hello') assert next(itr) == 'H' assert next(itr) == 'E' assert list(itr) == list('LLO') You can use this approach unless you are defining a custom iterator as a
subclass of a base class defined elsewhere that does not derive from
from builtins import next from some_module import some_base_class class Upper2(some_base_class): def __init__(self, iterable): self._iter = iter(iterable) def __next__(self): # Py3-style iterator interface return next(self._iter).upper() def __iter__(self): return self itr2 = Upper2('hello') assert next(itr2) == 'H' assert next(itr2) == 'E'
itr3 = iter(['one', 'three', 'five']) assert 'next' in dir(itr3) assert next(itr3) == 'one' This approach is feasible whenever your code calls the itr2 = Upper2('hello') assert next(itr2) == 'H' assert next(itr2) == 'E' assert list(itr2) == list('LLO') # fails because Py2 implicitly looks # for a ``next`` method. Instead, you can use a decorator called from future.utils import implements_iterator Upper2 = implements_iterator(Upper2) print(list(Upper2('hello'))) # prints ['H', 'E', 'L', 'L', 'O'] This can of course also be used with the @implements_iterator class Upper2(some_base_class): def __init__(self, iterable): self._iter = iter(iterable) def __next__(self): # note the Py3 interface return next(self._iter).upper() def __iter__(self): return self On Python 3, as usual, this decorator does nothing. Binding a method to a class¶Python 2 draws a distinction between bound and unbound methods, whereas
in Python 3 this distinction is gone: unbound methods have been removed
from the language. To bind a method to a class compatibly across Python
3 and Python 2, you can use the from future.utils import bind_method class Greeter(object): pass def greet(self, message): print(message) bind_method(Greeter, 'greet', greet) g = Greeter() g.greet('Hi!') On Python 3, calling import types setattr(cls, name, types.MethodType(func, None, cls)) Metaclasses¶Python 3 and Python 2 syntax for metaclasses are incompatible.
from future.utils import with_metaclass class BaseForm(object): pass class FormType(type): pass class Form(with_metaclass(FormType, BaseForm)): pass |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |