Я пытаюсь перенести свой пакет Python3 на Python2. Я использовал pasteurize
и все работает нормально. Я заметил, что он импортировал несколько вещей из builtins
, включая str
, int
и super
. Мне было интересно, безопасно ли импортировать все из builtins
. Да, я знаю, что в принципе импорт звездочки считается плохой практикой, потому что это загромождает текущее пространство имен, не дает понять, что импортируется, и переопределяет имена, которые вы не собираетесь делать. Но когда дело доходит до builtins
, разве не правда, что все они уже присутствуют в виде имен, безопасны для импорта и ничего не ломают?
Кроме того, при использовании super
из builtins
в коде Python2 безопасно ли называть его super
Python3 без аргументов? Существуют ли крайние случаи, когда он может сломаться с Python2?
from builtins import * # is this frowned upon?
from future import standard_library
standard_library.install_aliases()
class Foo(object):
def __init__(self):
super().__init__() # is this always safe in python 2?
Изменить 1: Просто чтобы уточнить, для Python2 builtins
происходит от future
и не является встроенным модулем, как в Python3.
Редактировать 2: некоторые люди предполагают, что вызов super
без аргументов никогда работает и что импорт из builtins
не имеет значения. Очевидно, это неправильно.
from __future__ import print_function
class Foo(object):
def __init__(self, x):
self.x = x
class Bar(object):
def __init__(self, x):
self.x = x + 1
class Baz(Bar, Foo):
def __init__(self, x):
super().__init__(x)
try:
b = Baz(1)
except TypeError as e:
# this will only happen in Python 2
print("Didn't work: {}; trying with builtins.super".format(str(e)))
from builtins import super
b = Baz(1) # this now works in Python 2.7 too
print(b.x)
@mvp это просто неправда. Это не повлияет на производительность.
@DanielRoseman: НЕ делать что-то дополнительное, очевидно, быстрее, чем делать это?
Можете ли вы уточнить @DavisHerring? Разве будущее newsuper
не должно делать именно это: включить без аргументов super
?
@AaylaSecura: Нет, ты прав: я просто подумал, что ты имеешь в виду «встроенный super
», но ты имел в виду именно то, что сказал.
Этот super является повторной реализацией супера Python 3, который работает, но не ожидайте, что он будет таким же эффективным, как супер Python 3.
В Python 3 компилятор жульничает, и всякий раз, когда он видит имя super
, на которое ссылается функция, он автоматически добавляет в функцию переменную ячейки с именем __class__
. Функция super
использует __class__
для компенсации аргументов, которые ей не передаются. Вы можете увидеть это в действии, выполнив что-то вроде:
class X:
def f(self):
super
return __class__
assert X().f() is X
assert X.f.__closure__[0].cell_contents is X
__class__
определяется один раз (при первой компиляции функции)+, поэтому этот поиск, который выполняет super, очень быстрый.
newsuper
, с другой стороны, необходимо каждый раз углубляться в MRO (и любые декораторы), чтобы выяснить тип self
и тип, который был определен функцией. Кажется, это хорошо для обратного портирования Python 3 (наверное, поэтому он есть в future.builtins
). Но в противном случае вы должны придерживаться стандартного Python 2super
, чтобы люди, читающие ваш код, не удивлялись этому.
Реализация: (взято из https://github.com/rfk/magicssuper/blob/master/magicsuper/_super.py (как задокументировано future.builtins.newsuper
))
def super(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
'''Like builtin super(), but capable of magic.
This acts just like the builtin super() function, but if called
without any arguments it attempts to infer them at runtime.
'''
# Infer the correct call if used without arguments.
if typ is _SENTINEL:
# We'll need to do some frame hacking.
f = sys._getframe(framedepth)
try:
# Get the function's first positional argument.
type_or_obj = f.f_locals[f.f_code.co_varnames[0]]
except (IndexError,KeyError,):
raise RuntimeError('super() used in a function with no args')
try:
# Get the MRO so we can crawl it.
mro = type_or_obj.__mro__
except AttributeError:
try:
mro = type_or_obj.__class__.__mro__
except AttributeError:
raise RuntimeError('super() used with a non-newstyle class')
# A ``for...else`` block? Yes! It's odd, but useful.
# If unfamiliar with for...else, see:
#
# http://psung.blogspot.com/2007/12/for-else-in-python.html
for typ in mro:
# Find the class that owns the currently-executing method.
for meth in typ.__dict__.itervalues():
# Drill down through any wrappers to the underlying func.
# This handles e.g. classmethod() and staticmethod().
try:
while not isinstance(meth,FunctionType):
try:
meth = meth.__func__
except AttributeError:
meth = meth.__get__(type_or_obj)
except (AttributeError, TypeError):
continue
if meth.func_code is f.f_code:
break # Aha! Found you.
else:
continue # Not found! Move onto the next class in MRO.
break # Found! Break out of the search loop.
else:
raise RuntimeError('super() called outside a method')
# Dispatch to builtin super().
if type_or_obj is not _SENTINEL:
return _builtin_super(typ,type_or_obj)
return _builtin_super(typ)
Будут (странные) случаи, когда такая подделка super
будет иметь другое поведение, например, обезьяна, исправляющая класс из другого класса. Вероятно, это не проблема, но стоит помнить.
Это было бы плохо с точки зрения производительности. Импортируйте только то, что вам действительно нужно.