如何从仅影响一个类的内置函数中导入对象?

Posted

技术标签:

【中文标题】如何从仅影响一个类的内置函数中导入对象?【英文标题】:How to import object from builtins affecting just one class? 【发布时间】:2019-02-04 07:39:32 【问题描述】:

我正在使用futurenewstyle 类的代码从python2 转换为python3。我的项目在 Django 1.11 中

我在 forms.py 中有一个类:

class Address:
    ...rest of code...

class AddressForm(Address, forms.ModelForm):
    ...rest of code...

在 Python 2 中

转换为:

from buitlins import object
class Address(object):
        ...rest of code...

class AddressForm(Address, forms.ModelForm):
    ...rest of code...

在 Python 3 中

我有一个 selenium 测试,当这个表单在转换为 Python3 后被调用时失败,并出现以下错误:

File "<path_to_venv>/local/lib/python2.7/site-packages/django/utils/six.py", line 842, in <lambda>
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
File "<path_to_venv>/local/lib/python2.7/site-packages/future/types/newobject.py", line 78, in __unicode__
s = type(self).__str__(self)
RuntimeError: maximum recursion depth exceeded

但是,当我删除导入 from buitlins import object 时,测试通过了。

但由于我添加了未来检查,我得到一个未来差异错误,因此每个类都必须转换为 newstyle。我希望它在 Python2 和 Python3 中都能工作。

这个模块builtins 模块导入有没有办法只影响forms.py 文件中的一个类而不影响其他类。还是有其他方法可以解决这个问题?

【问题讨论】:

我觉得你搞错了。 class Address: 应该在 Python 3 中使用,class Address(object) 应该在 Python2 中使用 class Address:class Address(object) 在 Python 3 中完全相同执行相同的操作,无论您是否从 builtins 显式导入 object 您的错误来自 Python 2.7 库,如果从 Python 3 使用该库可能无法正常工作。 这些类之一是否定义了next__next__ 方法?我们能看到它们吗? 不,没有next__next__ 方法@PatrickHaugh 【参考方案1】:

今天遇到这个问题,Patrick Haugh 主要描述了这个问题,除了在 django 1.11 中没有引用six 和python_2_unicode_compatible,问题中的版本和我正在使用的版本。在我们的例子中,问题在于 django 模型继承自 mixin,而 mixin 继承自 future.builtins.newobject

    newobject (from builtins import object) 添加了一个名为 unicode:https://github.com/PythonCharmers/python-future/blob/master/src/future/types/newobject.py#L41 django admin 有一个日志记录功能,可以创建一个 LogEntry,其中包含一个 对象的文本表示。 https://github.com/django/django/blob/stable/1.11.x/django/contrib/admin/options.py#L741 使用的文本表示是object.__unicode__:https://github.com/django/django/blob/stable/1.11.x/django/utils/encoding.py#L77 __unicode__ 是未来包中 __str__ 的包装 __str__ 用于 django db 模型被实现为 django https://github.com/django/django/blob/stable/1.11.x/django/db/models/base.py#L595 中 unicode 的包装器

我们没有一个很好的解决方案,除了如果我们需要访问两者,明确地将未来导入为from builtins import object as future_object,并通过运行futurize --stage2 -x object而不是futurize --stage2来禁用整个修复

【讨论】:

【参考方案2】:

您遇到的问题似乎来自两个不同的 Python 2 现代化工具的冲突。您似乎正在使用来自django.utils.sixpython_2_unicode_compatible 装饰器

def python_2_unicode_compatible(klass):
    """
    A decorator that defines __unicode__ and __str__ methods under Python 2.
    Under Python 3 it does nothing.
    To support Python 2 and 3 with a single code base, define a __str__ method
    returning text and apply this decorator to the class.
    """
    if PY2:
        if '__str__' not in klass.__dict__:
            raise ValueError("@python_2_unicode_compatible cannot be applied "
                             "to %s because it doesn't define __str__()." %
                             klass.__name__)
        klass.__unicode__ = klass.__str__
        klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
    return klass

并继承自newobject,它有这个__unicode__ 方法

def __unicode__(self):
    # All subclasses of the builtin object should have __str__ defined.
    # Note that old-style classes do not have __str__ defined.
    if hasattr(self, '__str__'):
        s = type(self).__str__(self)
    else:
        s = str(self)
    if isinstance(s, unicode):
        return s
    else:
        return s.decode('utf-8')

并且由于两者在提供__unicode____str__ 方法方面的策略略有不同,因此它们会无限地相互调用,这会导致您的递归错误。

提供builtins.object 的模块提供了自己的python_2_unicode_compatible 装饰器。您是否尝试过使用来自django.utils.six 的那个?

【讨论】:

我试过class Address(object): 没有from builtins import object 并且测试通过了。但是由于newstyle在CI CD的检查列表中,它需要from builtins import object,否则futurize错误会有所不同。 提供builtins.object的模块提供了自己的python_2_unicode_compatible装饰器。您是否尝试过使用来自django.utils.six 的那个? 刚刚试过这个python_2_unicode_compatible,装饰器工作了。非常感谢。这真的很有帮助:)【参考方案3】:

这是python2的方式。

class Address(object):

在python3类中隐式继承对象,所以应该是这样的;

class Address:

【讨论】:

old-style and new-style classes in Python 2.7? 不是反过来吗?从 py2 迁移到 py3 在 Python 2 中,class Address: 是旧式类,class Address(object): 是新式类。在 Python 3 中,所有的类都是新式的。对于转换,您应该将所有类更改为 class Adddress(object):from builtins import object 正在导入一个 shim,它知道 Python 3 的接口,如 Python 2 所没有的 __str____next__ @PatrickHaugh 这是有道理的。但我仍然想知道正在转换为 newstyle 的类不会在任何地方使用 __str____next__ object 定义了一个__str__ 方法,因此您的Address(object) 类将继承该方法。

以上是关于如何从仅影响一个类的内置函数中导入对象?的主要内容,如果未能解决你的问题,请参考以下文章

Python标准库:内置函数type(object)

反射(高大上)类的内置方法

JSP的内置对象的调用函数介绍

内置函数 对象是类的实例

内置函数拾遗

python-面向对象速查表-内置方法-内置函数-内置属性(只整理了部分内容)