标签归档:oop

如何在Python中创建对象的副本?

问题:如何在Python中创建对象的副本?

我想创建一个对象的副本。我希望新对象拥有旧对象的所有属性(字段的值)。但是我想拥有独立的对象。因此,如果我更改新对象的字段的值,则旧对象不应受此影响。

I would like to create a copy of an object. I want the new object to possess all properties of the old object (values of the fields). But I want to have independent objects. So, if I change values of the fields of the new object, the old object should not be affected by that.


回答 0

要获得对象的完全独立副本,可以使用copy.deepcopy()函数。

有关浅层复制和深层复制的更多详细信息,请参考该问题的其他答案以及该答案中对相关问题的很好的解释。

To get a fully independent copy of an object you can use the copy.deepcopy() function.

For more details about shallow and deep copying please refer to the other answers to this question and the nice explanation in this answer to a related question.


回答 1

如何在Python中创建对象的副本?

因此,如果我更改新对象的字段的值,则旧对象不应受此影响。

那你的意思是一个可变的对象。

在Python 3中,列表获取copy方法(在2中,您将使用切片来制作副本):

>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True

浅副本

浅拷贝只是最外层容器的拷贝。

list.copy 是浅表副本:

>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

您不会获得内部对象的副本。它们是同一个对象-因此,对它们进行突变后,更改会显示在两个容器中。

深拷贝

深层副本是每个内部对象的递归副本。

>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

所做的更改不会反映在原始文档中,而只会反映在副本中。

不变的对象

不可变对象通常不需要复制。实际上,如果您尝试这样做,Python只会为您提供原始对象:

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'

元组甚至没有复制方法,因此让我们用一个切片尝试一下:

>>> tuple_copy_attempt = a_tuple[:]

但是我们看到它是同一对象:

>>> tuple_copy_attempt is a_tuple
True

对于字符串类似:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

对于冻结集,即使它们具有以下copy方法:

>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True

何时复制不可变对象

如果需要复制可变的内部对象,则应复制不可变对象。

>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)

如我们所见,当副本的内部对象发生突变时,原始对象不会更改。

自定义对象

定制对象通常将数据存储在__dict__属性中或__slots__(类似元组的内存结构中)中。

要制作可复制的对象,请定义__copy__(对于浅层副本)和/或__deepcopy__(对于深层副本)。

from copy import copy, deepcopy

class Copyable:
    __slots__ = 'a', '__dict__'
    def __init__(self, a, b):
        self.a, self.b = a, b
    def __copy__(self):
        return type(self)(self.a, self.b)
    def __deepcopy__(self, memo): # memo is a dict of id's to copies
        id_self = id(self)        # memoization avoids unnecesary recursion
        _copy = memo.get(id_self)
        if _copy is None:
            _copy = type(self)(
                deepcopy(self.a, memo), 
                deepcopy(self.b, memo))
            memo[id_self] = _copy 
        return _copy

请注意,deepcopy保留id(original)要复制的(或身份编号)备忘录字典。为了享受递归数据结构的良好行为,请确保您尚未制作副本,如果有,请返回该副本。

因此,让我们创建一个对象:

>>> c1 = Copyable(1, [2])

copy进行浅表复制:

>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]

deepcopy现在做了深刻的副本:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]

How can I create a copy of an object in Python?

So, if I change values of the fields of the new object, the old object should not be affected by that.

You mean a mutable object then.

In Python 3, lists get a copy method (in 2, you’d use a slice to make a copy):

>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True

Shallow Copies

Shallow copies are just copies of the outermost container.

list.copy is a shallow copy:

>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

You don’t get a copy of the interior objects. They’re the same object – so when they’re mutated, the change shows up in both containers.

Deep copies

Deep copies are recursive copies of each interior object.

>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

Changes are not reflected in the original, only in the copy.

Immutable objects

Immutable objects do not usually need to be copied. In fact, if you try to, Python will just give you the original object:

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'

Tuples don’t even have a copy method, so let’s try it with a slice:

>>> tuple_copy_attempt = a_tuple[:]

But we see it’s the same object:

>>> tuple_copy_attempt is a_tuple
True

Similarly for strings:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

and for frozensets, even though they have a copy method:

>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True

When to copy immutable objects

Immutable objects should be copied if you need a mutable interior object copied.

>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)

As we can see, when the interior object of the copy is mutated, the original does not change.

Custom Objects

Custom objects usually store data in a __dict__ attribute or in __slots__ (a tuple-like memory structure.)

To make a copyable object, define __copy__ (for shallow copies) and/or __deepcopy__ (for deep copies).

from copy import copy, deepcopy

class Copyable:
    __slots__ = 'a', '__dict__'
    def __init__(self, a, b):
        self.a, self.b = a, b
    def __copy__(self):
        return type(self)(self.a, self.b)
    def __deepcopy__(self, memo): # memo is a dict of id's to copies
        id_self = id(self)        # memoization avoids unnecesary recursion
        _copy = memo.get(id_self)
        if _copy is None:
            _copy = type(self)(
                deepcopy(self.a, memo), 
                deepcopy(self.b, memo))
            memo[id_self] = _copy 
        return _copy

Note that deepcopy keeps a memoization dictionary of id(original) (or identity numbers) to copies. To enjoy good behavior with recursive data structures, make sure you haven’t already made a copy, and if you have, return that.

So let’s make an object:

>>> c1 = Copyable(1, [2])

And copy makes a shallow copy:

>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]

And deepcopy now makes a deep copy:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]

回答 2

浅复制 copy.copy()

#!/usr/bin/env python3

import copy

class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]

# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]

# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]

深度复制 copy.deepcopy()

#!/usr/bin/env python3
import copy
class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]

文档:https : //docs.python.org/3/library/copy.html

在Python 3.6.5上测试。

Shallow copy with copy.copy()

#!/usr/bin/env python3

import copy

class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]

# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]

# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]

Deep copy with copy.deepcopy()

#!/usr/bin/env python3
import copy
class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]

Documentation: https://docs.python.org/3/library/copy.html

Tested on Python 3.6.5.


回答 3

我相信以下内容应适用于许多行为良好的Python类:

def copy(obj):
    return type(obj)(obj)

(当然,我在这里不是在谈论“深层副本”,这是一个不同的故事,并且可能不是一个非常清晰的概念-足够深的深度?)

根据我对Python 3的测试,对于不可变的对象(例如元组或字符串),它返回相同的对象(因为不需要对不可变的对象进行浅表复制),但是对于列表或字典,它创建了独立的浅表复制。

当然,此方法仅适用于构造函数具有相应行为的类。可能的用例:制作标准Python容器类的浅表副本。

I believe the following should work with many well-behaved classed in Python:

def copy(obj):
    return type(obj)(obj)

(Of course, I am not talking here about “deep copies,” which is a different story, and which may be not a very clear concept — how deep is deep enough?)

According to my tests with Python 3, for immutable objects, like tuples or strings, it returns the same object (because there is no need to make a shallow copy of an immutable object), but for lists or dictionaries it creates an independent shallow copy.

Of course this method only works for classes whose constructors behave accordingly. Possible use cases: making a shallow copy of a standard Python container class.


为什么需要在Python方法中显式包含“ self”参数?

问题:为什么需要在Python方法中显式包含“ self”参数?

在Python中的类上定义方法时,它看起来像这样:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

但是在某些其他语言(例如C#)中,您可以使用“ this”关键字来引用该方法所绑定的对象,而无需在方法原型中将其声明为参数。

这是Python中的一种故意的语言设计决策,还是有一些实现细节需要传递“ self”作为参数?

When defining a method on a class in Python, it looks something like this:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

But in some other languages, such as C#, you have a reference to the object that the method is bound to with the “this” keyword without declaring it as an argument in the method prototype.

Was this an intentional language design decision in Python or are there some implementation details that require the passing of “self” as an argument?


回答 0

我喜欢引用Peters的Python Zen。“显式比隐式好。”

在Java和C ++中,this.可以推断出’ ‘,除非您拥有无法推断的变量名。因此,您有时需要它,有时则不需要。

Python选择使这种事情变得明确,而不是基于规则。

另外,由于没有暗示或假设,因此公开了部分实现。 self.__class__self.__dict__以及其他“内部”结构也很明显。

I like to quote Peters’ Zen of Python. “Explicit is better than implicit.”

In Java and C++, ‘this.‘ can be deduced, except when you have variable names that make it impossible to deduce. So you sometimes need it and sometimes don’t.

Python elects to make things like this explicit rather than based on a rule.

Additionally, since nothing is implied or assumed, parts of the implementation are exposed. self.__class__, self.__dict__ and other “internal” structures are available in an obvious way.


回答 1

这是为了最小化方法和函数之间的差异。它使您可以轻松地在元类中生成方法,或在运行时将方法添加到预先存在的类中。

例如

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

据我所知,这也使python运行时的实现更加容易。

It’s to minimize the difference between methods and functions. It allows you to easily generate methods in metaclasses, or add methods at runtime to pre-existing classes.

e.g.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

It also (as far as I know) makes the implementation of the python runtime easier.


回答 2

我建议人们应该阅读Guido van Rossum关于此主题的博客为什么必须保留显性自我

当修饰一个方法定义时,我们不知道是否要自动给它一个“自我”参数:修饰器可以将函数变成静态方法(没有“自我”)或类方法(其中有一个有趣的自我,它引用一个类而不是一个实例),或者可以做一些完全不同的事情(编写在纯Python中实现“ @classmethod”或“ @staticmethod”的装饰器是微不足道的)。没有办法不知道装饰器的作用,是否赋予被定义的方法一个隐式的“自我”参数。

我拒绝诸如特殊外壳“ @classmethod”和“ @staticmethod”之类的hack。

I suggest that one should read Guido van Rossum’s blog on this topic – Why explicit self has to stay.

When a method definition is decorated, we don’t know whether to automatically give it a ‘self’ parameter or not: the decorator could turn the function into a static method (which has no ‘self’), or a class method (which has a funny kind of self that refers to a class instead of an instance), or it could do something completely different (it’s trivial to write a decorator that implements ‘@classmethod’ or ‘@staticmethod’ in pure Python). There’s no way without knowing what the decorator does whether to endow the method being defined with an implicit ‘self’ argument or not.

I reject hacks like special-casing ‘@classmethod’ and ‘@staticmethod’.


回答 3

Python不会强迫您使用“自我”。您可以根据需要命名。您只需要记住,方法定义标头中的第一个参数是对该对象的引用。

Python doesn’t force you on using “self”. You can give it whatever name you want. You just have to remember that the first argument in a method definition header is a reference to the object.


回答 4

还允许您执行此操作:(简而言之,调用Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)将返回12,但将以最疯狂的方式返回。

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

当然,用Java和C#这样的语言很难想象这一点。通过使自引用明确,您可以自由地通过该自引用引用任何对象。而且,在更静态的语言中很难用这种在运行时玩类的方式-并不是说它一定是好是坏。只是外在的自我允许所有这些疯狂存在。

此外,想象一下:我们想自定义方法的行为(用于概要分析或某种疯狂的黑魔法)。这可以使我们思考:如果我们拥有一个Method可以覆盖或控制其行为的类怎么办?

好吧,这是:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

而现在:InnocentClass().magic_method()将像预期的那样运行。该方法将与的innocent_self参数绑定InnocentClass,并与magic_selfMagicMethod实例的绑定。奇怪吗?就像有2个关键字this1以及this2Java和C#这样的语言一样。像这样的魔术使框架能够执行原本会更加冗长的工作。

同样,我不想评论这种东西的道德。我只是想展示在没有明确的自我参考的情况下很难做的事情。

Also allows you to do this: (in short, invoking Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) will return 12, but will do so in the craziest of ways.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Of course, this is harder to imagine in languages like Java and C#. By making the self reference explicit, you’re free to refer to any object by that self reference. Also, such a way of playing with classes at runtime is harder to do in the more static languages – not that’s it’s necessarily good or bad. It’s just that the explicit self allows all this craziness to exist.

Moreover, imagine this: We’d like to customize the behavior of methods (for profiling, or some crazy black magic). This can lead us to think: what if we had a class Method whose behavior we could override or control?

Well here it is:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

And now: InnocentClass().magic_method() will act like expected. The method will be bound with the innocent_self parameter to InnocentClass, and with the magic_self to the MagicMethod instance. Weird huh? It’s like having 2 keywords this1 and this2 in languages like Java and C#. Magic like this allows frameworks to do stuff that would otherwise be much more verbose.

Again, I don’t want to comment on the ethics of this stuff. I just wanted to show things that would be harder to do without an explicit self reference.


回答 5

我认为,除了“ Python之禅”之外,真正的原因还在于,函数是Python中的一等公民。

本质上使它们成为对象。现在的根本问题是,如果您的函数也是对象,那么在面向对象的范例中,当消息本身是对象时,如何将消息发送给对象?

看起来像一个鸡蛋问题,为了减少这种矛盾,唯一可能的方法是将执行上下文传递给方法或对其进行检测。但是由于python可以具有嵌套函数,因此将不可能做到这一点,因为内部函数的执行上下文将发生变化。

这意味着唯一可能的解决方案是显式传递“ self”(执行的上下文)。

因此,我认为Zen来得晚了,这是一个实现问题。

I think the real reason besides “The Zen of Python” is that Functions are first class citizens in Python.

Which essentially makes them an Object. Now The fundamental issue is if your functions are object as well then, in Object oriented paradigm how would you send messages to Objects when the messages themselves are objects ?

Looks like a chicken egg problem, to reduce this paradox, the only possible way is to either pass a context of execution to methods or detect it. But since python can have nested functions it would be impossible to do so as the context of execution would change for inner functions.

This means the only possible solution is to explicitly pass ‘self’ (The context of execution).

So i believe it is a implementation problem the Zen came much later.


回答 6

我认为这与PEP 227有关:

类范围内的名称不可访问。名称在最里面的函数范围内解析。如果类定义出现在嵌套作用域链中,则解析过程将跳过类定义。此规则可防止类属性和局部变量访问之间发生奇怪的交互。如果在类定义中发生了名称绑定操作,它将在结果类对象上创建一个属性。要在方法或方法中嵌套的函数中访问此变量,必须通过self或通过类名使用属性引用。

I think it has to do with PEP 227:

Names in class scope are not accessible. Names are resolved in the innermost enclosing function scope. If a class definition occurs in a chain of nested scopes, the resolution process skips class definitions. This rule prevents odd interactions between class attributes and local variable access. If a name binding operation occurs in a class definition, it creates an attribute on the resulting class object. To access this variable in a method, or in a function nested within a method, an attribute reference must be used, either via self or via the class name.


回答 7

Python中的self所述,Demystified

像obj.meth(args)之类的东西都变成Class.meth(obj,args)。调用过程是自动的,而接收过程不是(它的显式)。这就是类中函数的第一个参数必须是对象本身的原因。

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

调用:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init()定义了三个参数,但我们只传递了两个(6和8)。同样,distance()要求传递一个但零个参数。

为什么Python不抱怨此参数编号不匹配

通常,当我们调用带有某些参数的方法时,通过将方法的对象放在第一个参数之前来调用相应的类函数。因此,像obj.meth(args)之类的东西都会变成Class.meth(obj,args)。调用过程是自动的,而接收过程不是(它的显式)。

这就是类中函数的第一个参数必须是对象本身的原因。将此参数写为self只是一种约定。它不是关键字,在Python中没有特殊含义。我们可以使用其他名称(例如这样),但我强烈建议您不要使用。对于大多数开发人员来说,使用除self之外的其他名称并不受欢迎,这会降低代码的可读性(“可读性计数”)。

在第一个示例中,self.x是实例属性,而x是局部变量。它们不相同,并且位于不同的命名空间中。

自我在这里停留

许多人建议将self用作Python的关键字,例如C ++和Java。这将消除方法中形式参数列表中显式自我的多余使用。尽管这个想法看起来很有希望,但它不会发生。至少在不久的将来不会。主要原因是向后兼容。这是Python的创建者本人写的博客,解释了为何必须保留显式自我。

As explained in self in Python, Demystified

anything like obj.meth(args) becomes Class.meth(obj, args). The calling process is automatic while the receiving process is not (its explicit). This is the reason the first parameter of a function in class must be the object itself.

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocations:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init() defines three parameters but we just passed two (6 and 8). Similarly distance() requires one but zero arguments were passed.

Why is Python not complaining about this argument number mismatch?

Generally, when we call a method with some arguments, the corresponding class function is called by placing the method’s object before the first argument. So, anything like obj.meth(args) becomes Class.meth(obj, args). The calling process is automatic while the receiving process is not (its explicit).

This is the reason the first parameter of a function in class must be the object itself. Writing this parameter as self is merely a convention. It is not a keyword and has no special meaning in Python. We could use other names (like this) but I strongly suggest you not to. Using names other than self is frowned upon by most developers and degrades the readability of the code (“Readability counts”).

In, the first example self.x is an instance attribute whereas x is a local variable. They are not the same and lie in different namespaces.

Self Is Here To Stay

Many have proposed to make self a keyword in Python, like this in C++ and Java. This would eliminate the redundant use of explicit self from the formal parameter list in methods. While this idea seems promising, it’s not going to happen. At least not in the near future. The main reason is backward compatibility. Here is a blog from the creator of Python himself explaining why the explicit self has to stay.


回答 8

还有一个非常简单的答案:根据python的禅定,“显式优于隐式”。

There is also another very simple answer: according to the zen of python, “explicit is better than implicit”.


如何在python中实现接口?

问题:如何在python中实现接口?

public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

如何实现与C#代码等效的Python?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

这是一个好主意吗??请在您的答案中举例说明。

public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

How do I implement Python equivalent of this C# code ?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

Is this a good idea?? Please give examples in your answers.


回答 0

正如其他人在这里提到的:

在Python中不需要接口。这是因为Python具有适当的多重继承,还具有鸭式输入法,这意味着必须在Java中具有接口的地方,而不必在Python中具有接口。

也就是说,接口还有多种用途。其中一些被Python 2.6中引入的Pythons抽象基类覆盖。如果要创建无法实例化但提供特定接口或实现的一部分的基类,则它们很有用。

另一种用法是,如果您以某种方式想要指定一个对象实现特定的接口,并且可以通过从它们的子类中使用ABC来实现。另一种方法是zope.interface,它是Zope组件体系结构(一个非常酷的组件框架)的一部分的模块。在这里,您不是从接口子类化,而是将类(甚至实例)标记为实现接口。这也可以用于从组件注册表中查找组件。超酷!

As mentioned by other here:

Interfaces are not necessary in Python. This is because Python has proper multiple inheritance, and also ducktyping, which means that the places where you must have interfaces in Java, you don’t have to have them in Python.

That said, there are still several uses for interfaces. Some of them are covered by Pythons Abstract Base Classes, introduced in Python 2.6. They are useful, if you want to make base classes that cannot be instantiated, but provide a specific interface or part of an implementation.

Another usage is if you somehow want to specify that an object implements a specific interface, and you can use ABC’s for that too by subclassing from them. Another way is zope.interface, a module that is a part of the Zope Component Architecture, a really awesomely cool component framework. Here you don’t subclass from the interfaces, but instead mark classes (or even instances) as implementing an interface. This can also be used to look up components from a component registry. Supercool!


回答 1

将abc模块用于抽象基类似乎可以解决问题。

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()

Using the abc module for abstract base classes seems to do the trick.

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()

回答 2

介面支援Python 2.7和Python 3.4+。

安装界面,您必须

pip install python-interface

示例代码:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y

interface supports Python 2.7 and Python 3.4+.

To install interface you have to

pip install python-interface

Example Code:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y

回答 3

在现代Python 3中,使用抽象基类实现接口要简单得多,它们的目的是作为插件扩展的接口协定。

创建接口/抽象基类:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

创建一个普通的子类并覆盖所有抽象方法:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

您可以选择在抽象方法中使用通用实现,如所述create_sale_invoice()super()在上述子类中显式调用它。

没有实现所有抽象方法的子类的实例化失败:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

通过将相应的注释与结合使用,您还可以拥有抽象属性,静态方法和类方法@abstractmethod

抽象基类非常适合实现基于插件的系统。可以通过访问所有类的所有导入子类__subclasses__(),因此,如果从插件目录中加载所有类,importlib.import_module()并且它们是基类的子类,则可以通过直接访问它们,__subclasses__()并且可以确保对所有它们在实例化期间。

这是AccountingSystem上面示例的插件加载实现:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

然后,您可以通过访问会计系统插件对象 AccountingSystem该类:

>>> accountingsystem = AccountingSystem.instance()

(受此PyMOTW-3帖子启发。)

Implementing interfaces with abstract base classes is much simpler in modern Python 3 and they serve a purpose as an interface contract for plug-in extensions.

Create the interface/abstract base class:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

Create a normal subclass and override all abstract methods:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

You can optionally have common implementation in the abstract methods as in create_sale_invoice(), calling it with super() explicitly in the subclass as above.

Instantiation of a subclass that does not implement all the abstract methods fails:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

You can also have abstract properties, static and class methods by combining corresponding annotations with @abstractmethod.

Abstract base classes are great for implementing plugin-based systems. All imported subclasses of a class are accessible via __subclasses__(), so if you load all classes from a plugin directory with importlib.import_module() and if they subclass the base class, you have direct access to them via __subclasses__() and you can be sure that the interface contract is enforced for all of them during instantiation.

Here’s the plugin loading implementation for the AccountingSystem example above:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

Then you can access the accounting system plugin object through the AccountingSystem class:

>>> accountingsystem = AccountingSystem.instance()

(Inspired by this PyMOTW-3 post.)


回答 4

有用于Python的第三方接口实现(最受欢迎的是Zope’s,也用于Twisted中),但是更常见的是,Python编码人员更喜欢使用称为“抽象基类”(ABC)的更丰富的概念,该概念将接口与那里也有一些实施方面的可能性。在2.6和更高版本的Python 中,特别好地支持ABC,请参阅PEP,但即使在早期版本的Python中,它们也通常被视为“前进的道路”-只需定义一个类,某些类的方法将引发,NotImplementedError这样子类将成为注意,他们最好重写这些方法!-)

There are third-party implementations of interfaces for Python (most popular is Zope’s, also used in Twisted), but more commonly Python coders prefer to use the richer concept known as an “Abstract Base Class” (ABC), which combines an interface with the possibility of having some implementation aspects there too. ABCs are particularly well supported in Python 2.6 and later, see the PEP, but even in earlier versions of Python they’re normally seen as “the way to go” — just define a class some of whose methods raise NotImplementedError so that subclasses will be on notice that they’d better override those methods!-)


回答 5

像这样的东西(可能无法正常工作,因为我没有Python):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

Something like this (might not work as I don’t have Python around):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"

回答 6

我的理解是,在像Python这样的动态语言中,接口不是必需的。在Java(或带有抽象基类的C ++)中,接口是用于确保例如传递正确的参数,能够执行一组任务的方法。

例如,如果您具有观察者并且是可观察的,那么可观察的对象就是订阅支持IObserver接口的对象,而该对象又会起作用notify。这是在编译时检查的。

在Python中,没有这样的事情,compile time并且在运行时执行方法查找。而且,可以使用__getattr __()或__getattribute __()魔术方法覆盖查找。换句话说,您可以作为观察者传递可以在访问notify属性时返回可调用对象的任何对象。

这使我得出结论,Python中的接口确实存在 -只是它们的执行被推迟到实际使用它们的那一刻

My understanding is that interfaces are not that necessary in dynamic languages like Python. In Java (or C++ with its abstract base class) interfaces are means for ensuring that e.g. you’re passing the right parameter, able to perform set of tasks.

E.g. if you have observer and observable, observable is interested in subscribing objects that supports IObserver interface, which in turn has notify action. This is checked at compile time.

In Python, there is no such thing as compile time and method lookups are performed at runtime. Moreover, one can override lookup with __getattr__() or __getattribute__() magic methods. In other words, you can pass, as observer, any object that can return callable on accessing notify attribute.

This leads me to the conclusion, that interfaces in Python do exist – it’s just their enforcement is postponed to the moment in which they are actually used


解释Python的’__enter__’和’__exit__’

问题:解释Python的’__enter__’和’__exit__’

我在某人的代码中看到了这一点。这是什么意思?

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s


print s

I saw this in someone’s code. What does it mean?

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s


print s

回答 0

使用这些魔术方法(__enter____exit__)使您可以实现可轻松用于该with语句的对象。

这个想法是,它使得构建需要执行一些“清除”代码的代码(将其视为一个try-finally块)变得容易。这里有更多的解释

一个有用的例子是数据库连接对象(一旦对应的“ with”语句超出范围,它就会自动关闭连接):

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

如上所述,将此对象与with语句一起使用(from __future__ import with_statement如果您使用的是Python 2.5,则可能需要在文件顶部执行此操作)。

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343-‘with’语句’也有不错的写法。

Using these magic methods (__enter__, __exit__) allows you to implement objects which can be used easily with the with statement.

The idea is that it makes it easy to build code which needs some ‘cleandown’ code executed (think of it as a try-finally block). Some more explanation here.

A useful example could be a database connection object (which then automagically closes the connection once the corresponding ‘with’-statement goes out of scope):

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

As explained above, use this object with the with statement (you may need to do from __future__ import with_statement at the top of the file if you’re on Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 — The ‘with’ statement’ has a nice writeup as well.


回答 1

如果您知道上下文管理器是什么,那么您就不需要了解__enter__和使用__exit__魔术方法。让我们看一个非常简单的例子。

在此示例中,我将借助open函数打开myfile.txt。在尝试/终于块确保即使发生意外的异常myfile.txt的将被关闭。

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

现在我用with语句打开相同的文件:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

如果您查看代码,则我没有关闭文件,也没有try / finally块。因为with语句会自动关闭 myfile.txt。您甚至可以通过调用print(fp.closed)attribute 来检查它-返回True

这是因为open函数返回的文件对象(在我的示例中为fp)具有两个内置方法__enter____exit__。它也称为上下文管理器。__enter__方法在with块的开头__exit__ 被调用,方法在结尾被调用。注意:with语句仅适用于支持上下文管理协议的对象,即它们具有__enter____exit__方法。实现这两种方法的类称为上下文管理器类。

现在让我们定义自己的上下文管理器类。

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

我希望您现在对魔术方法__enter__和两者都有基本的了解__exit__

If you know what context managers are then you need nothing more to understand __enter__ and __exit__ magic methods. Lets see a very simple example.

In this example I am opening myfile.txt with help of open function. The try/finally block ensures that even if an unexpected exception occurs myfile.txt will be closed.

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

Now I am opening same file with with statement:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

If you look at the code, I didn’t close the file & there is no try/finally block. Because with statement automatically closes myfile.txt . You can even check it by calling print(fp.closed) attribute — which returns True.

This is because the file objects (fp in my example) returned by open function has two built-in methods __enter__ and __exit__. It is also known as context manager. __enter__ method is called at the start of with block and __exit__ method is called at the end. Note: with statement only works with objects that support the context mamangement protocol i.e. they have __enter__ and __exit__ methods. A class which implement both methods is known as context manager class.

Now lets define our own context manager class.

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

I hope now you have basic understanding of both __enter__ and __exit__ magic methods.


回答 2

我发现通过Googling 定位python文档__enter____exit__方法非常困难,因此,这里的链接可以帮助其他人:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(两个版本的细节相同)

object.__enter__(self)
输入与此对象相关的运行时上下文。该with语句会将此方法的返回值绑定到该语句的as子句中指定的目标(如果有)。

object.__exit__(self, exc_type, exc_value, traceback)
退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文无exceptions地退出,则所有三个参数均为None

如果提供了异常,并且该方法希望抑制该异常(即,防止其传播),则它应返回一个真值。否则,将在退出此方法后正常处理异常。

注意,__exit__()方法不应该引发传入的异常。这是调用方法的责任。

我希望对__exit__方法参数进行清楚的描述。这是缺少的,但是我们可以推断出它们。

大概exc_type是异常的类。

它说您不应该重新引发传入的异常。这向我们表明,其中一个参数可能是实际的Exception实例……或者您应该自己从类型和值中实例化它?

我们可以通过看这篇文章来回答:http :
//effbot.org/zone/python-with-statement.htm

例如,以下__exit__方法可吞噬所有TypeError,但允许所有其他异常通过:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

…显然value是Exception实例。

大概traceback是一个Python 回溯对象。

I found it strangely difficult to locate the python docs for __enter__ and __exit__ methods by Googling, so to help others here is the link:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(detail is the same for both versions)

object.__enter__(self)
Enter the runtime context related to this object. The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.

object.__exit__(self, exc_type, exc_value, traceback)
Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.

I was hoping for a clear description of the __exit__ method arguments. This is lacking but we can deduce them…

Presumably exc_type is the class of the exception.

It says you should not re-raise the passed-in exception. This suggests to us that one of the arguments might be an actual Exception instance …or maybe you’re supposed to instantiate it yourself from the type and value?

We can answer by looking at this article:
http://effbot.org/zone/python-with-statement.htm

For example, the following __exit__ method swallows any TypeError, but lets all other exceptions through:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

…so clearly value is an Exception instance.

And presumably traceback is a Python traceback object.


回答 3

除了上述答案以例证调用顺序外,一个简单的运行示例

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

产生输出:

__init__
__enter__
body
__exit__
__del__

提醒:使用语法时,在上述情况下with myclass() as mc,变量mc获取由返回的值!对于此类用途,需要定义返回值,例如:__enter__()None

def __enter__(self): 
    print('__enter__')
    return self

In addition to the above answers to exemplify invocation order, a simple run example

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

Produces the output:

__init__
__enter__
body
__exit__
__del__

A reminder: when using the syntax with myclass() as mc, variable mc gets the value returned by __enter__(), in the above case None! For such use, need to define return value, such as:

def __enter__(self): 
    print('__enter__')
    return self

回答 4

尝试添加我的答案(我的学习思想):

__enter__[__exit__]两者都是在“ with语句 ” 主体(PEP 343)的入口处和出口处被调用的方法,并且两者的实现都称为上下文管理器。

with语句旨在隐藏try finally子句的流控制,并使代码难以理解。

with语句的语法为:

with EXPR as VAR:
    BLOCK

转换为(如PEP 343中所述):

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

尝试一些代码:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

然后手动尝试(遵循翻译语法):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

服务器端的结果与以前相同

对不起,我的英语不好,解释不清楚,谢谢。

try adding my answers (my thought of learning) :

__enter__ and [__exit__] both are methods that are invoked on entry to and exit from the body of “the with statement” (PEP 343) and implementation of both is called context manager.

the with statement is intend to hiding flow control of try finally clause and make the code inscrutable.

the syntax of the with statement is :

with EXPR as VAR:
    BLOCK

which translate to (as mention in PEP 343) :

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

try some code:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

and now try manually (following translate syntax):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

the result of the server side same as before

sorry for my bad english and my unclear explanations, thank you….


回答 5

这称为上下文管理器,我只想补充一下,其他编程语言也存在类似的方法。比较它们可能有助于理解python中的上下文管理器。基本上,当我们处理一些需要初始化的资源(文件,网络,数据库),并且在某些时候需要拆除(配置)时,使用上下文管理器。在Java 7及更高版本中,我们具有以下形式的自动资源管理:

//Java code
try (Session session = new Session())
{
  // do stuff
}

请注意,会话需要实现AutoClosable其(许多)子接口之一。

C#中,我们使用using语句来管理资源,其形式为:

//C# code
using(Session session = new Session())
{
  ... do stuff.
}

Session应该在其中实施IDisposable

python中,我们使用的类应实现__enter____exit__。因此它采取以下形式:

#Python code
with Session() as session:
    #do stuff

正如其他人指出的那样,您始终可以在所有语言中使用try / finally语句来实现相同的机制。这只是语法糖。

This is called context manager and I just want to add that similar approaches exist for other programming languages. Comparing them could be helpful in understanding the context manager in python. Basically, a context manager is used when we are dealing with some resources (file, network, database) that need to be initialized and at some point, tear downed (disposed). In Java 7 and above we have automatic resource management that takes the form of:

//Java code
try (Session session = new Session())
{
  // do stuff
}

Note that Session needs to implement AutoClosable or one of its (many) sub-interfaces.

In C#, we have using statements for managing resources that takes the form of:

//C# code
using(Session session = new Session())
{
  ... do stuff.
}

In which Session should implement IDisposable.

In python, the class that we use should implement __enter__ and __exit__. So it takes the form of:

#Python code
with Session() as session:
    #do stuff

And as others pointed out, you can always use try/finally statement in all the languages to implement the same mechanism. This is just syntactic sugar.


向现有对象实例添加方法

问题:向现有对象实例添加方法

我读过,可以在Python中向现有对象(即不在类定义中)添加方法。

我了解这样做并不总是一件好事。但是怎么可能呢?

I’ve read that it is possible to add a method to an existing object (i.e., not in the class definition) in Python.

I understand that it’s not always good to do so. But how might one do this?


回答 0

在Python中,函数和绑定方法之间存在差异。

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

绑定方法已“绑定”(具有描述性)到实例,并且只要调用该方法,该实例将作为第一个参数传递。

但是,作为类(而不是实例)的属性的可调用对象仍未绑定,因此您可以在需要时修改类定义:

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

先前定义的实例也会被更新(只要它们本身没有覆盖属性):

>>> a.fooFighters()
fooFighters

当您要将方法附加到单个实例时,就会出现问题:

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

该函数直接附加到实例时不会自动绑定:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

要绑定它,我们可以在类型模块中使用MethodType函数

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

这次,该类的其他实例没有受到影响:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

通过阅读有关描述符元类 编程的信息,可以找到更多信息。

In Python, there is a difference between functions and bound methods.

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

Bound methods have been “bound” (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.

Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

Previously defined instances are updated as well (as long as they haven’t overridden the attribute themselves):

>>> a.fooFighters()
fooFighters

The problem comes when you want to attach a method to a single instance:

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

The function is not automatically bound when it’s attached directly to an instance:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

To bind it, we can use the MethodType function in the types module:

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

This time other instances of the class have not been affected:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

More information can be found by reading about descriptors and metaclass programming.


回答 1

自python 2.6起不推荐使用new模块,并在3.0版中将其删除,请使用类型

参见http://docs.python.org/library/new.html

在下面的示例中,我故意从patch_me()函数中删除了返回值。我认为提供返回值可能会使人相信patch返回了一个新对象,这是不正确的-它修改了传入的对象。可能这可以促进对Monkey补丁的更严格的使用。

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>

Module new is deprecated since python 2.6 and removed in 3.0, use types

see http://docs.python.org/library/new.html

In the example below I’ve deliberately removed return value from patch_me() function. I think that giving return value may make one believe that patch returns a new object, which is not true – it modifies the incoming one. Probably this can facilitate a more disciplined use of monkeypatching.

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>

回答 2

前言-有关兼容性的说明:其他答案可能仅在Python 2中有效-此答案在Python 2和3中应该可以很好地工作。如果仅编写Python 3,则可能会显式地继承自object,但是代码应保持不变。

向现有对象实例添加方法

我读过,可以在Python中向现有对象(例如不在类定义中)添加方法。

我了解这样做并非总是一个好的决定。但是,怎么可能呢?

是的,有可能-但不建议

我不建议这样做。这是一个坏主意。不要这样

这有两个原因:

  • 您将向执行此操作的每个实例添加一个绑定对象。如果您经常这样做,则可能会浪费大量内存。通常仅在调用的短时间内创建绑定方法,然后在自动垃圾回收时它们不再存在。如果手动执行此操作,则将具有一个引用绑定方法的名称绑定-这将防止使用时对其进行垃圾回收。
  • 给定类型的对象实例通常在该类型的所有对象上都有其方法。如果在其他位置添加方法,则某些实例将具有那些方法,而其他实例则不会。程序员不会期望如此,您可能会违反最不惊奇规则
  • 由于还有其他非常好的理由不这样做,因此,如果这样做,您的声誉也会很差。

因此,我建议您除非有充分的理由,否则不要这样做。这是更好的在类定义来定义的正确方法更少的类直接优选猴的贴剂,是这样的:

Foo.sample_method = sample_method

由于具有指导意义,因此,我将向您展示一些这样做的方法。

怎么做

这是一些设置代码。我们需要一个类定义。可以将其导入,但这并不重要。

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

创建一个实例:

foo = Foo()

创建一个添加方法:

def sample_method(self, bar, baz):
    print(bar + baz)

方法零(0)-使用描述符方法, __get__

在函数上进行的点分查找__get__使用实例调用函数的方法,将对象绑定到该方法,从而创建“绑定方法”。

foo.sample_method = sample_method.__get__(foo)

现在:

>>> foo.sample_method(1,2)
3

方法一-types.MethodType

首先,导入类型,从中我们将获得方法构造函数:

import types

现在我们将方法添加到实例中。为此,我们需要types模块(上面已导入)中的MethodType构造函数。

types.MethodType的参数签名为(function, instance, class)

foo.sample_method = types.MethodType(sample_method, foo, Foo)

和用法:

>>> foo.sample_method(1,2)
3

方法二:词法绑定

首先,我们创建一个包装器函数,将方法绑定到实例:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

用法:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

方法三:functools.partial

局部函数将第一个参数应用于函数(以及可选的关键字参数),以后可以与其余参数(以及覆盖的关键字参数)一起调用。从而:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

当您认为绑定方法是实例的部分功能时,这很有意义。

未绑定函数作为对象属性-为什么不起作用:

如果尝试以与将其添加到类中相同的方式添加sample_method,则它不受实例约束,并且不会将隐式self作为第一个参数。

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

我们可以通过显式传递实例(或其他任何方法,因为此方法实际上不使用self参数变量)来使未绑定函数起作用,但是它将与其他实例的预期签名不一致(如果我们进行了Monkey修补)此实例):

>>> foo.sample_method(foo, 1, 2)
3

结论

现在,您知道可以执行此操作的几种方法,但是,认真地说-不要这样做。

Preface – a note on compatibility: other answers may only work in Python 2 – this answer should work perfectly well in Python 2 and 3. If writing Python 3 only, you might leave out explicitly inheriting from object, but otherwise the code should remain the same.

Adding a Method to an Existing Object Instance

I’ve read that it is possible to add a method to an existing object (e.g. not in the class definition) in Python.

I understand that it’s not always a good decision to do so. But, how might one do this?

Yes, it is possible – But not recommended

I don’t recommend this. This is a bad idea. Don’t do it.

Here’s a couple of reasons:

  • You’ll add a bound object to every instance you do this to. If you do this a lot, you’ll probably waste a lot of memory. Bound methods are typically only created for the short duration of their call, and they then cease to exist when automatically garbage collected. If you do this manually, you’ll have a name binding referencing the bound method – which will prevent its garbage collection on usage.
  • Object instances of a given type generally have its methods on all objects of that type. If you add methods elsewhere, some instances will have those methods and others will not. Programmers will not expect this, and you risk violating the rule of least surprise.
  • Since there are other really good reasons not to do this, you’ll additionally give yourself a poor reputation if you do it.

Thus, I suggest that you not do this unless you have a really good reason. It is far better to define the correct method in the class definition or less preferably to monkey-patch the class directly, like this:

Foo.sample_method = sample_method

Since it’s instructive, however, I’m going to show you some ways of doing this.

How it can be done

Here’s some setup code. We need a class definition. It could be imported, but it really doesn’t matter.

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

Create an instance:

foo = Foo()

Create a method to add to it:

def sample_method(self, bar, baz):
    print(bar + baz)

Method nought (0) – use the descriptor method, __get__

Dotted lookups on functions call the __get__ method of the function with the instance, binding the object to the method and thus creating a “bound method.”

foo.sample_method = sample_method.__get__(foo)

and now:

>>> foo.sample_method(1,2)
3

Method one – types.MethodType

First, import types, from which we’ll get the method constructor:

import types

Now we add the method to the instance. To do this, we require the MethodType constructor from the types module (which we imported above).

The argument signature for types.MethodType is (function, instance, class):

foo.sample_method = types.MethodType(sample_method, foo, Foo)

and usage:

>>> foo.sample_method(1,2)
3

Method two: lexical binding

First, we create a wrapper function that binds the method to the instance:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

usage:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

Method three: functools.partial

A partial function applies the first argument(s) to a function (and optionally keyword arguments), and can later be called with the remaining arguments (and overriding keyword arguments). Thus:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

This makes sense when you consider that bound methods are partial functions of the instance.

Unbound function as an object attribute – why this doesn’t work:

If we try to add the sample_method in the same way as we might add it to the class, it is unbound from the instance, and doesn’t take the implicit self as the first argument.

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

We can make the unbound function work by explicitly passing the instance (or anything, since this method doesn’t actually use the self argument variable), but it would not be consistent with the expected signature of other instances (if we’re monkey-patching this instance):

>>> foo.sample_method(foo, 1, 2)
3

Conclusion

You now know several ways you could do this, but in all seriousness – don’t do this.


回答 3

我认为以上答案错过了关键点。

让我们来一个带有方法的类:

class A(object):
    def m(self):
        pass

现在,让我们在ipython中玩它:

In [2]: A.m
Out[2]: <unbound method A.m>

好的,因此m()以某种方式成为A的未绑定方法。但是真的是那样吗?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

事实证明,m()只是一个函数,对它的引用已添加到A类字典中-没有魔术。那为什么要给我们一个不受约束的方法?这是因为该点未转换为简单的字典查找。实际上是A .__ class __.__ getattribute __(A,’m’)的调用:

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

现在,我不确定为什么最后一行要打印两次,但是仍然很清楚那里发生了什么。

现在,默认的__getattribute__所做的是检查属性是否为所谓的描述符,即,是否实现了特殊的__get__方法。如果实现该方法,则返回该__get__方法的结果。回到我们的A类的第一个版本,这是我们拥有的:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

而且由于Python函数实现了描述符协议,所以如果它们代表一个对象被调用,它们将通过__get__方法将自身绑定到该对象。

好的,如何为现有对象添加方法?假设您不介意修补类,那么它很简单:

B.m = m

然后,借助描述符魔术,Bm “成为”一个不受约束的方法。

而且,如果您只想向单个对象添加方法,则必须使用types.MethodType来自己模拟机制。

b.m = types.MethodType(m, b)

顺便说说:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>

I think that the above answers missed the key point.

Let’s have a class with a method:

class A(object):
    def m(self):
        pass

Now, let’s play with it in ipython:

In [2]: A.m
Out[2]: <unbound method A.m>

Ok, so m() somehow becomes an unbound method of A. But is it really like that?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

It turns out that m() is just a function, reference to which is added to A class dictionary – there’s no magic. Then why A.m gives us an unbound method? It’s because the dot is not translated to a simple dictionary lookup. It’s de facto a call of A.__class__.__getattribute__(A, ‘m’):

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

Now, I’m not sure out of the top of my head why the last line is printed twice, but still it’s clear what’s going on there.

Now, what the default __getattribute__ does is that it checks if the attribute is a so-called descriptor or not, i.e. if it implements a special __get__ method. If it implements that method, then what is returned is the result of calling that __get__ method. Going back to the first version of our A class, this is what we have:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

And because Python functions implement the descriptor protocol, if they are called on behalf of an object, they bind themselves to that object in their __get__ method.

Ok, so how to add a method to an existing object? Assuming you don’t mind patching class, it’s as simple as:

B.m = m

Then B.m “becomes” an unbound method, thanks to the descriptor magic.

And if you want to add a method just to a single object, then you have to emulate the machinery yourself, by using types.MethodType:

b.m = types.MethodType(m, b)

By the way:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>

回答 4

在Python中,Monkey修补通常通过覆盖您自己的类或函数签名来起作用。以下是Zope Wiki的示例:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

该代码将覆盖/创建一个在类上称为“讲话”的方法。在杰夫·阿特伍德(Jeff Atwood)最近关于Monkey修补的文章中。他显示了C#3.0中的示例,这是我在工作中使用的当前语言。

In Python monkey patching generally works by overwriting a class or functions signature with your own. Below is an example from the Zope Wiki:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

That code will overwrite/create a method called speak on the class. In Jeff Atwood’s recent post on monkey patching. He shows an example in C# 3.0 which is the current language I use for work.


回答 5

您可以使用lambda将方法绑定到实例:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

输出:

This is instance string

You can use lambda to bind a method to an instance:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

Output:

This is instance string

回答 6

没有至少一种方法可以将方法附加到实例types.MethodType

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

有用的链接:
数据模型-调用描述符描述
符方法指南-调用描述符

There are at least two ways for attach a method to an instance without types.MethodType:

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

Useful links:
Data model – invoking descriptors
Descriptor HowTo Guide – invoking descriptors


回答 7

setattr我相信您在寻找什么。使用此设置对象上的属性。

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>

What you’re looking for is setattr I believe. Use this to set an attribute on an object.

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>

回答 8

由于此问题要求使用非Python版本,因此以下是JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }

Since this question asked for non-Python versions, here’s JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }

回答 9

通过查看不同绑定方法的结果,合并Jason Pratt和社区Wiki的答案:

尤其要注意将绑定函数添加为类方法的工作原理,但是引用范围不正确。

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

就个人而言,我更喜欢使用外部ADDMETHOD函数路由,因为它也允许我在迭代器中动态分配新的方法名称。

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>

Consolidating Jason Pratt’s and the community wiki answers, with a look at the results of different methods of binding:

Especially note how adding the binding function as a class method works, but the referencing scope is incorrect.

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

Personally, I prefer the external ADDMETHOD function route, as it allows me to dynamically assign new method names within an iterator as well.

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>

回答 10

这实际上是“杰森·普拉特”答案的附加内容

尽管杰森斯(Jasons)回答有效,但只有在要向类中添加函数时才起作用。当我尝试从.py源代码文件中重新加载现有方法时,它对我不起作用。

我花了很长时间才找到解决方法,但是这个技巧似乎很简单… 1.st从源代码文件导入代码2.nd强制重新加载3.rd使用types.FunctionType(…)来转换导入并绑定到函数的方法,您还可以传递当前的全局变量,因为重新加载的方法将位于不同的命名空间4.现在,您可以按照类型的“ Jason Pratt”的建议继续使用type.MethodType(… )

例:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"

    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code

This is actually an addon to the answer of “Jason Pratt”

Although Jasons answer works, it does only work if one wants to add a function to a class. It did not work for me when I tried to reload an already existing method from the .py source code file.

It took me for ages to find a workaround, but the trick seems simple… 1.st import the code from the source code file 2.nd force a reload 3.rd use types.FunctionType(…) to convert the imported and bound method to a function you can also pass on the current global variables, as the reloaded method would be in a different namespace 4.th now you can continue as suggested by “Jason Pratt” using the types.MethodType(…)

Example:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"

    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code

回答 11

如果有什么帮助,我最近发布了一个名为Gorilla的Python库,以使Monkey修补过程更加方便。

使用函数needle()来修补名为的模块的过程guineapig如下:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

但它也需要照顾的更有趣的使用情况如图所示FAQ文档

该代码可在GitHub上获得

If it can be of any help, I recently released a Python library named Gorilla to make the process of monkey patching more convenient.

Using a function needle() to patch a module named guineapig goes as follows:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

But it also takes care of more interesting use cases as shown in the FAQ from the documentation.

The code is available on GitHub.


回答 12

这个问题是几年前提出的,但是,有一种简单的方法可以使用装饰器来模拟函数与类实例的绑定:

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

在那里,当您将函数和实例传递给活页夹装饰器时,它将创建一个新函数,其代码对象与第一个相同。然后,该类的给定实例存储在新创建的函数的属性中。装饰器返回一个(第三个)函数,该函数自动调用复制的函数,并将实例作为第一个参数。

总之,您将获得一个模拟它绑定到类实例的函数。保留原始功能不变。

This question was opened years ago, but hey, there’s an easy way to simulate the binding of a function to a class instance using decorators:

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

There, when you pass the function and the instance to the binder decorator, it will create a new function, with the same code object as the first one. Then, the given instance of the class is stored in an attribute of the newly created function. The decorator return a (third) function calling automatically the copied function, giving the instance as the first parameter.

In conclusion you get a function simulating it’s binding to the class instance. Letting the original function unchanged.


回答 13

Jason Pratt发表的内容是正确的。

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

如您所见,Python认为b()与a()没有什么不同。在Python中,所有方法只是碰巧是函数的变量。

What Jason Pratt posted is correct.

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

As you can see, Python doesn’t consider b() any different than a(). In Python all methods are just variables that happen to be functions.


回答 14

我感到奇怪的是,没有人提到上面列出的所有方法都会在添加的方法和实例之间创建一个循环引用,从而导致对象在垃圾回收之前一直保持不变。有一个古老的技巧通过扩展对象的类来添加描述符:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

I find it strange that nobody mentioned that all of the methods listed above creates a cycle reference between the added method and the instance, causing the object to be persistent till garbage collection. There was an old trick adding a descriptor by extending the class of the object:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

回答 15

from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

有了这个,你可以使用self指针

from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

With this, you can use the self pointer


“ super”在Python中做什么?

问题:“ super”在Python中做什么?

之间有什么区别:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

和:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

我看到super在只有单一继承的类中经常使用它。我知道为什么您会在多重继承中使用它,但不清楚在这种情况下使用它的好处。

What’s the difference between:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

and:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

I’ve seen super being used quite a lot in classes with only single inheritance. I can see why you’d use it in multiple inheritance but am unclear as to what the advantages are of using it in this kind of situation.


回答 0

super()单一继承的好处很小-大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中。

但是,如果不使用,几乎不可能使用多重继承super()。这包括常见的惯用语,例如mixin,接口,抽象类等。这扩展到了以后扩展您的代码的代码。如果以后有人要编写扩展的类Child和mixin,则他们的代码将无法正常工作。

The benefits of super() in single-inheritance are minimal — mostly, you don’t have to hard-code the name of the base class into every method that uses its parent methods.

However, it’s almost impossible to use multiple-inheritance without super(). This includes common idioms like mixins, interfaces, abstract classes, etc. This extends to code that later extends yours. If somebody later wanted to write a class that extended Child and a mixin, their code would not work properly.


回答 1

有什么不同?

SomeBaseClass.__init__(self) 

表示呼叫SomeBaseClass的方式__init__。而

super(Child, self).__init__()

表示__init__Child实例的方法解析顺序(MRO)中遵循的父类调用绑定。

如果实例是Child的子类,则MRO中可能紧随其后的是另一个父级。

简单解释

当编写一个类时,您希望其他类能够使用它。super()使其他类更容易使用您正在编写的类。

正如鲍勃·马丁(Bob Martin)所说,好的架构可以使您尽可能长地推迟决策。

super() 可以实现这种架构。

当另一个类对您编写的类进行子类化时,它也可能继承自其他类。而这些类可以有一个__init__在此之后来自__init__基于类的进行方法解析顺序。

如果没有super您,可能会硬编码您正在编写的类的父级(如示例中所示)。这意味着您将不会__init__在MRO中调用下一个,因此您将无法重用其中的代码。

如果您正在编写自己的代码供个人使用,则可能不必担心这种区别。但是,如果您希望其他人使用您的代码,则使用super一件事可以为代码用户提供更大的灵活性。

Python 2与3

这适用于Python 2和3:

super(Child, self).__init__()

这仅适用于Python 3:

super().__init__()

它不带任何参数,方法是在堆栈框架中上移并获取方法的第一个参数(通常self用于实例方法或cls类方法-但可以是其他名称),然后Child在自由变量中找到类(例如)(__class__在方法中将其作为自由闭合变量的名称进行查找)。

我更喜欢演示使用的交叉兼容方式super,但是如果您仅使用Python 3,则可以不带任何参数调用它。

具有前向兼容性的间接

它给你什么?对于单继承,从静态分析的角度来看,问题的示例实际上是相同的。但是,使用super会为您提供具有向前兼容性的间接层。

前向兼容性对经验丰富的开发人员非常重要。您希望代码在更改时保持最少的更改。当您查看修订历史记录时,您希望确切地看到更改的时间。

您可以从单一继承开始,但是如果您决定添加另一个基类,则只需要更改基数行即可-如果基类在您继承的类中发生了变化(例如添加了mixin),则可以进行更改这个班没什么。特别是在Python 2中,super很难正确获取参数和正确的方法参数。如果您知道super正确地使用了单继承,那么调试就不会那么困难了。

依赖注入

其他人可以使用您的代码并将父级注入方法解析中:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

假设您向对象添加了另一个类,并想在Foo和Bar之间注入一个类(出于测试或其他原因):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

使用un-super子级无法注入依赖项,因为您正在使用的子级已经硬编码了要在其自身之后调用的方法:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

但是,带有子级的类super可以正确注入依赖项:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

发表评论

为什么这在世界上有用?

Python通过C3线性化算法将复杂的继承树线性化,以创建方法解析顺序(MRO)。

我们希望按该顺序查找方法。

对于在父级中定义的方法,如果不按顺序查找下一个方法super,则必须

  1. 从实例的类型获取mro
  2. 寻找定义方法的类型
  3. 用该方法找到下一个类型
  4. 绑定该方法并使用所需的参数调用它

UnsuperChild不该访问InjectMe。为什么没有“总是避免使用super” 的结论?我在这里想念什么?

UnsuperChild不会访问InjectMe。可以UnsuperInjector访问InjectMe-却无法从其继承的方法调用该类的方法UnsuperChild

两个Child类都打算使用MRO中紧随其后的相同名称来调用方法,这可能是它在创建时不知道的另一个类。

没有super硬编码其父方法的方法-因此限制了其方法的行为,并且子类无法在调用链中注入功能。

在一个 super具有更大的灵活性。这些方法的调用链可以被拦截并注入功能。

您可能不需要该功能,但是代码的子类却可能需要。

结论

始终使用super引用父类而不是对其进行硬编码。

您打算引用的是下一行的父类,而不是您看到子级继承的父类。

不使用super会给您的代码用户带来不必要的限制。

What’s the difference?

SomeBaseClass.__init__(self) 

means to call SomeBaseClass‘s __init__. while

super(Child, self).__init__()

means to call a bound __init__ from the parent class that follows Child in the instance’s Method Resolution Order (MRO).

If the instance is a subclass of Child, there may be a different parent that comes next in the MRO.

Explained simply

When you write a class, you want other classes to be able to use it. super() makes it easier for other classes to use the class you’re writing.

As Bob Martin says, a good architecture allows you to postpone decision making as long as possible.

super() can enable that sort of architecture.

When another class subclasses the class you wrote, it could also be inheriting from other classes. And those classes could have an __init__ that comes after this __init__ based on the ordering of the classes for method resolution.

Without super you would likely hard-code the parent of the class you’re writing (like the example does). This would mean that you would not call the next __init__ in the MRO, and you would thus not get to reuse the code in it.

If you’re writing your own code for personal use, you may not care about this distinction. But if you want others to use your code, using super is one thing that allows greater flexibility for users of the code.

Python 2 versus 3

This works in Python 2 and 3:

super(Child, self).__init__()

This only works in Python 3:

super().__init__()

It works with no arguments by moving up in the stack frame and getting the first argument to the method (usually self for an instance method or cls for a class method – but could be other names) and finding the class (e.g. Child) in the free variables (it is looked up with the name __class__ as a free closure variable in the method).

I prefer to demonstrate the cross-compatible way of using super, but if you are only using Python 3, you can call it with no arguments.

Indirection with Forward Compatibility

What does it give you? For single inheritance, the examples from the question are practically identical from a static analysis point of view. However, using super gives you a layer of indirection with forward compatibility.

Forward compatibility is very important to seasoned developers. You want your code to keep working with minimal changes as you change it. When you look at your revision history, you want to see precisely what changed when.

You may start off with single inheritance, but if you decide to add another base class, you only have to change the line with the bases – if the bases change in a class you inherit from (say a mixin is added) you’d change nothing in this class. Particularly in Python 2, getting the arguments to super and the correct method arguments right can be difficult. If you know you’re using super correctly with single inheritance, that makes debugging less difficult going forward.

Dependency Injection

Other people can use your code and inject parents into the method resolution:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

Say you add another class to your object, and want to inject a class between Foo and Bar (for testing or some other reason):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

Using the un-super child fails to inject the dependency because the child you’re using has hard-coded the method to be called after its own:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

However, the class with the child that uses super can correctly inject the dependency:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Addressing a comment

Why in the world would this be useful?

Python linearizes a complicated inheritance tree via the C3 linearization algorithm to create a Method Resolution Order (MRO).

We want methods to be looked up in that order.

For a method defined in a parent to find the next one in that order without super, it would have to

  1. get the mro from the instance’s type
  2. look for the type that defines the method
  3. find the next type with the method
  4. bind that method and call it with the expected arguments

The UnsuperChild should not have access to InjectMe. Why isn’t the conclusion “Always avoid using super“? What am I missing here?

The UnsuperChild does not have access to InjectMe. It is the UnsuperInjector that has access to InjectMe – and yet cannot call that class’s method from the method it inherits from UnsuperChild.

Both Child classes intend to call a method by the same name that comes next in the MRO, which might be another class it was not aware of when it was created.

The one without super hard-codes its parent’s method – thus is has restricted the behavior of its method, and subclasses cannot inject functionality in the call chain.

The one with super has greater flexibility. The call chain for the methods can be intercepted and functionality injected.

You may not need that functionality, but subclassers of your code may.

Conclusion

Always use super to reference the parent class instead of hard-coding it.

What you intend is to reference the parent class that is next-in-line, not specifically the one you see the child inheriting from.

Not using super can put unnecessary constraints on users of your code.


回答 2

我与玩了一点super(),并意识到我们可以更改通话顺序。

例如,我们有下一个层次结构:

    A
   / \
  B   C
   \ /
    D

在这种情况下,D的MRO将是(仅适用于Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

让我们创建一个super()方法执行后调用的类。

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

因此,我们可以看到解析顺序与MRO中的解析顺序相同。但是当我们super()在方法的开头调用时:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

我们有一个不同的顺序,它是MRO元组的相反顺序。

    A
   / 
  B  C
    /
    D 

如需其他阅读,我建议下一个答案:

  1. 具有超级(大型层次结构)的C3线性化示例
  2. 新旧样式类之间的重要行为更改
  3. 新型课堂的内幕故事

I had played a bit with super(), and had recognized that we can change calling order.

For example, we have next hierarchy structure:

    A
   / \
  B   C
   \ /
    D

In this case MRO of D will be (only for Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Let’s create a class where super() calls after method execution.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / ⇖
  B ⇒ C
   ⇖ /
    D

So we can see that resolution order is same as in MRO. But when we call super() in the beginning of the method:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

We have a different order it is reversed a order of the MRO tuple.

    A
   / ⇘
  B ⇐ C
   ⇘ /
    D 

For additional reading I would recommend next answers:

  1. C3 linearization example with super (a large hierarchy)
  2. Important behavior changes between old and new style classes
  3. The Inside Story on New-Style Classes

回答 3

难道不是所有这些都假设基类是新型类吗?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

在Python 2中将无法使用。class A必须是新样式,即:class A(object)

Doesn’t all of this assume that the base class is a new-style class?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

Will not work in Python 2. class A must be new-style, i.e: class A(object)


回答 4

当调用super()解析为父方法的类方法,实例方法或静态方法时,我们希望将其所在范围的当前类作为第一个参数传递,以指示我们要解析为哪个父方法的范围,并作为第二个参数是感兴趣的对象,用于指示我们要将该范围应用于哪个对象。

考虑一个类层次结构AB以及C其中,每个类是一个跟随它的父,并且abc每个的相应实例。

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

super与静态方法一起使用

例如super()__new__()方法中使用

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

说明:

1-尽管通常__new__()将对调用类的引用作为其第一个参数,但它不是在Python中作为类方法实现的,而是作为静态方法实现的。也就是说,在__new__()直接调用时,必须将对类的引用作为第一个参数显式传递:

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2-当调用super()到达父类时,我们将子类A作为其第一个参数传递,然后传递对感兴趣对象的引用,在这种情况下,它A.__new__(cls)是调用时传递的类引用。在大多数情况下,它也恰好是对子类的引用。在某些情况下,例如在多代继承的情况下,可能并非如此。

super(A, cls)

3-由于通常__new__()是静态方法,super(A, cls).__new__因此也将返回静态方法,并且需要显式提供所有参数,在这种情况下,包括对insterest对象的引用cls

super(A, cls).__new__(cls, *a, **kw)

4-没有做同样的事情 super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

使用super与实例方法

例如super()从内部使用__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

说明:

1- __init__是一个实例方法,这意味着它将实例的引用作为其第一个参数。当直接从实例调用时,引用将隐式传递,即您无需指定它:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2-当super()在内部调用时,__init__()我们将子类作为第一个参数,将感兴趣的对象作为第二个参数,这通常是对子类实例的引用。

super(A, self)

3-调用super(A, self)返回一个代理,它将解析作用域并将其应用于self当前的父类实例。让我们称该代理s。由于__init__()是实例方法,因此调用s.__init__(...)将隐式地将的引用self作为第一个参数传递给父级的__init__()

4-要做同样的事情,而super无需将对实例的引用显式传递给父版本__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

super与类方法一起使用

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

说明:

1-可以直接从类中调用类方法,并将对类的引用作为其第一个参数。

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

2-通话时 super()在类方法中以解析为其父级的版本时,我们希望将当前子类作为第一个参数传递,以指示我们要解析到的父级范围,而感兴趣的对象作为第二个参数指示我们要将该范围应用于哪个对象,通常是对子类本身或其子类之一的引用。

super(B, cls_or_subcls)

3-呼叫super(B, cls)解析到的范围A并将其应用于cls。由于alternate_constructor()是类方法,因此调用super(B, cls).alternate_constructor(...)将隐式传递的引用cls作为A的版本的第一个参数alternate_constructor()

super(B, cls).alternate_constructor()

4-要在不使用的情况下执行相同的操作super(),则需要获取未绑定版本的引用A.alternate_constructor()(即函数的显式版本)。简单地这样做是行不通的:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

上面的A.alternate_constructor()方法不起作用,因为该方法将隐式引用A作为其第一个参数。在cls这里传递的存在将是其第二个参数。

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)

When calling super() to resolve to a parent’s version of a classmethod, instance method, or staticmethod, we want to pass the current class whose scope we are in as the first argument, to indicate which parent’s scope we’re trying to resolve to, and as a second argument the object of interest to indicate which object we’re trying to apply that scope to.

Consider a class hierarchy A, B, and C where each class is the parent of the one following it, and a, b, and c respective instances of each.

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

Using super with a staticmethod

e.g. using super() from within the __new__() method

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

Explanation:

1- even though it’s usual for __new__() to take as its first param a reference to the calling class, it is not implemented in Python as a classmethod, but rather a staticmethod. That is, a reference to a class has to be passed explicitly as the first argument when calling __new__() directly:

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2- when calling super() to get to the parent class we pass the child class A as its first argument, then we pass a reference to the object of interest, in this case it’s the class reference that was passed when A.__new__(cls) was called. In most cases it also happens to be a reference to the child class. In some situations it might not be, for instance in the case of multiple generation inheritances.

super(A, cls)

3- since as a general rule __new__() is a staticmethod, super(A, cls).__new__ will also return a staticmethod and needs to be supplied all arguments explicitly, including the reference to the object of insterest, in this case cls.

super(A, cls).__new__(cls, *a, **kw)

4- doing the same thing without super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

Using super with an instance method

e.g. using super() from within __init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

Explanation:

1- __init__ is an instance method, meaning that it takes as its first argument a reference to an instance. When called directly from the instance, the reference is passed implicitly, that is you don’t need to specify it:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2- when calling super() within __init__() we pass the child class as the first argument and the object of interest as a second argument, which in general is a reference to an instance of the child class.

super(A, self)

3- The call super(A, self) returns a proxy that will resolve the scope and apply it to self as if it’s now an instance of the parent class. Let’s call that proxy s. Since __init__() is an instance method the call s.__init__(...) will implicitly pass a reference of self as the first argument to the parent’s __init__().

4- to do the same without super we need to pass a reference to an instance explicitly to the parent’s version of __init__().

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

Using super with a classmethod

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

Explanation:

1- A classmethod can be called from the class directly and takes as its first parameter a reference to the class.

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

2- when calling super() within a classmethod to resolve to its parent’s version of it, we want to pass the current child class as the first argument to indicate which parent’s scope we’re trying to resolve to, and the object of interest as the second argument to indicate which object we want to apply that scope to, which in general is a reference to the child class itself or one of its subclasses.

super(B, cls_or_subcls)

3- The call super(B, cls) resolves to the scope of A and applies it to cls. Since alternate_constructor() is a classmethod the call super(B, cls).alternate_constructor(...) will implicitly pass a reference of cls as the first argument to A‘s version of alternate_constructor()

super(B, cls).alternate_constructor()

4- to do the same without using super() you would need to get a reference to the unbound version of A.alternate_constructor() (i.e. the explicit version of the function). Simply doing this would not work:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

The above would not work because the A.alternate_constructor() method takes an implicit reference to A as its first argument. The cls being passed here would be its second argument.

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)

回答 5

有很多不错的答案,但是对于视觉学习者来说:首先让我们以super为参数进行探索,然后再以super为参数。

想象有一个jack从类创建的实例,该实例Jack具有继承链,如图中的绿色所示。调用:

super(Jack, jack).method(...)

将使用(jack按一定顺序的继承树)的MRO(方法解析顺序),并从开始搜索Jack。为什么可以提供家长班?好吧,如果我们从实例开始搜索jack,它将找到实例方法,重点是找到其父方法。

如果不向super提供参数,则其像传入的第一个参数是的类self,而传入的第二个参数是self。这些是在Python3中自动为您计算的。

但是请说我们不想使用Jack的方法,而不是传入Jack,我们可以传入从Jen开始向上搜索该方法Jen

它一次搜索一层(宽度而不是深度),例如,如果AdamSue两者都具有所需的方法,Sue将首先找到其中的一层。

如果CainSue都具有必需的方法,Cain则将首先调用的方法。这在代码中对应于:

Class Jen(Cain, Sue):

MRO是从左到右。

Many great answers, but for visual learners: Firstly lets explore with arguments to super, and then without.

Imagine theres an instance jack created from the class Jack, who has the inheritance chain as shown in green in the picture. Calling:

super(Jack, jack).method(...)

will use the MRO (Method Resolution Order) of jack (its inheritance tree in a certain order), and will start searching from Jack. Why can one provide a parent class? Well if we start searching from the instance jack, it would find the instance method, the whole point is to find its parents method.

If one does not supply arguments to super, its like the first argument passed in is the class of self, and the second argument passed in is self. These are auto-calculated for you in Python3.

However say we dont want to use Jack‘s method, instead of passing in Jack, we could of passed in Jen to start searching upwards for the method from Jen.

It searches one layer at a time (width not depth), e.g. if Adam and Sue both have the required method, the one from Sue will be found first.

If Cain and Sue both had the required method, Cain‘s method would be called first. This corresponds in code to:

Class Jen(Cain, Sue):

MRO is from left to right.


回答 6

这里有一些很好的答案,但是super()在层次结构中的不同类具有不同签名的情况下,它们并没有解决如何使用的问题……尤其是在__init__

为了回答这一部分并能够有效地使用,super()我建议阅读我的答案super()并更改合作方法的签名

这只是这种情况的解决方案:

  1. 层次结构中的顶级类必须继承自定义类,例如SuperObject
  2. 如果类可以采用不同的参数,则始终将您收到的所有参数作为关键字参数传递给超函数,并始终接受**kwargs
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

用法示例:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

输出:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

some great answers here, but they do not tackle how to use super() in the case where different classes in the hierarchy have different signatures … especially in the case of __init__

to answer that part and to be able to effectively use super() i’d suggest reading my answer super() and changing the signature of cooperative methods.

here’s just the solution to this scenario:

  1. the top-level classes in your hierarchy must inherit from a custom class like SuperObject:
  2. if classes can take differing arguments, always pass all arguments you received on to the super function as keyword arguments, and, always accept **kwargs.
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

example usage:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

output:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

回答 7

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

这很容易理解。

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

好的,如果您现在使用该super(Child,self)怎么办?

创建Child实例时,其MRO(方法解析顺序)基于继承的顺序为(Child,SomeBaseClass,对象)。(假设SomeBaseClass除默认对象外没有其他父对象)

通过传递Child, selfsuperself实例的MRO中搜索,然后返回Child的下一个代理对象(在本例中为SomeBaseClass),然后此对象调用__init__SomeBaseClass 的方法。换句话说,如果是super(SomeBaseClass,self),则super返回的代理对象将是object

对于多继承,MRO可以包含许多类,因此基本上super可以让您决定要在MRO中开始搜索的位置。

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

This is fairly easy to understand.

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

Ok, what happens now if you use super(Child,self)?

When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn’t have other parents except for the default object)

By passing Child, self, super searches in the MRO of the self instance, and return the proxy object next of Child, in this case it’s SomeBaseClass, this object then invokes the __init__ method of SomeBaseClass. In other word, if it’s super(SomeBaseClass,self), the proxy object that super returns would be object

For multi inheritance, the MRO could contain many classes, so basically super lets you decide where you want to start searching in the MRO.


回答 8

考虑以下代码:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

如果将的构造函数更改YX.__init__,您将获得:

X
Y
Q

但是使用super(Y, self).__init__(),您将获得:

X
P
Y
Q

P或者Q甚至可以从当你写你不知道另一个文件参与XY。因此,基本上,即使Y的签名与一样简单,您也不知道super(Child, self)在编写时将引用什么内容。这就是为什么超级可能是更好的选择。class Y(X)Y(X)

Consider the following code:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

If change constructor of Y to X.__init__, you will get:

X
Y
Q

But using super(Y, self).__init__(), you will get:

X
P
Y
Q

And P or Q may even be involved from another file which you don’t know when you writing X and Y. So, basically, you won’t know what super(Child, self) will reference to when you are writing class Y(X), even the signature of Y is as simple as Y(X). That’s why super could be a better choice.


__init__和__call__有什么区别?

问题:__init__和__call__有什么区别?

我想知道其中的差别之间__init____call__方法。

例如:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

I want to know the difference between __init__ and __call__ methods.

For example:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

回答 0

第一个用于初始化新创建的对象,并接收用于执行此操作的参数:

class Foo:
    def __init__(self, a, b, c):
        # ...

x = Foo(1, 2, 3) # __init__

第二个实现函数调用运算符。

class Foo:
    def __call__(self, a, b, c):
        # ...

x = Foo()
x(1, 2, 3) # __call__

The first is used to initialise newly created object, and receives arguments used to do that:

class Foo:
    def __init__(self, a, b, c):
        # ...

x = Foo(1, 2, 3) # __init__

The second implements function call operator.

class Foo:
    def __call__(self, a, b, c):
        # ...

x = Foo()
x(1, 2, 3) # __call__

回答 1

__call__()在元类中定义自定义方法允许将类的实例作为函数调用,而不必总是修改实例本身。

In [1]: class A:
   ...:     def __init__(self):
   ...:         print "init"
   ...:         
   ...:     def __call__(self):
   ...:         print "call"
   ...:         
   ...:         

In [2]: a = A()
init

In [3]: a()
call

Defining a custom __call__() method in the meta-class allows the class’s instance to be called as a function, not always modifying the instance itself.

In [1]: class A:
   ...:     def __init__(self):
   ...:         print "init"
   ...:         
   ...:     def __call__(self):
   ...:         print "call"
   ...:         
   ...:         

In [2]: a = A()
init

In [3]: a()
call

回答 2

在Python中,函数是一流的对象,这意味着:函数引用可以在输入中传递给其他函数和/或方法,并可以在它们内部执行。

可以将类的实例(也称为对象)当作函数来对待:将它们传递给其他方法/函数并调用它们。为了实现这一点,必须对__call__类函数进行专门化处理。

def __call__(self, [args ...]) 它以可变数量的参数作为输入。假定x是Class的实例Xx.__call__(1, 2)类似于调用实例x(1,2)将实例本身作为函数

在Python中,__init__()正确定义为Class Constructor(以及__del__()Class Destructor)。因此,__init__()和之间有一个净的区别__call__():第一个建立一个Class的实例,第二个使该实例作为一个函数可调用而不会影响对象本身的生命周期(即__call__,不影响构造/销毁生命周期),但是它可以修改其内部状态(如下所示)。

例。

class Stuff(object):

    def __init__(self, x, y, range):
        super(Stuff, self).__init__()
        self.x = x
        self.y = y
        self.range = range

    def __call__(self, x, y):
        self.x = x
        self.y = y
        print '__call__ with (%d,%d)' % (self.x, self.y)

    def __del__(self):
        del self.x
        del self.y
        del self.range

>>> s = Stuff(1, 2, 3)
>>> s.x
1
>>> s(7, 8)
__call__ with (7,8)
>>> s.x
7

In Python, functions are first-class objects, this means: function references can be passed in inputs to other functions and/or methods, and executed from inside them.

Instances of Classes (aka Objects), can be treated as if they were functions: pass them to other methods/functions and call them. In order to achieve this, the __call__ class function has to be specialized.

def __call__(self, [args ...]) It takes as an input a variable number of arguments. Assuming x being an instance of the Class X, x.__call__(1, 2) is analogous to calling x(1,2) or the instance itself as a function.

In Python, __init__() is properly defined as Class Constructor (as well as __del__() is the Class Destructor). Therefore, there is a net distinction between __init__() and __call__(): the first builds an instance of Class up, the second makes such instance callable as a function would be without impacting the lifecycle of the object itself (i.e. __call__ does not impact the construction/destruction lifecycle) but it can modify its internal state (as shown below).

Example.

class Stuff(object):

    def __init__(self, x, y, range):
        super(Stuff, self).__init__()
        self.x = x
        self.y = y
        self.range = range

    def __call__(self, x, y):
        self.x = x
        self.y = y
        print '__call__ with (%d,%d)' % (self.x, self.y)

    def __del__(self):
        del self.x
        del self.y
        del self.range

>>> s = Stuff(1, 2, 3)
>>> s.x
1
>>> s(7, 8)
__call__ with (7,8)
>>> s.x
7

回答 3

__call__使类的实例可调用。为什么会要求它?

从技术上讲__init____new__创建对象时会调用一次,以便可以对其进行初始化。

但是在许多情况下,您可能想重新定义对象,例如您已经完成了对象的工作,并可能需要一个新的对象。使用,__call__您可以像重新定义一样重新定义相同的对象。

这只是一种情况,可能还有更多。

__call__ makes the instance of a class callable. Why would it be required?

Technically __init__ is called once by __new__ when object is created, so that it can be initialized.

But there are many scenarios where you might want to redefine your object, say you are done with your object, and may find a need for a new object. With __call__ you can redefine the same object as if it were new.

This is just one case, there can be many more.


回答 4

>>> class A:
...     def __init__(self):
...         print "From init ... "
... 
>>> a = A()
From init ... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no __call__ method
>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call ... "
... 
>>> b = B()
From init ... 
>>> b()
From call ... 
>>> 
>>> class A:
...     def __init__(self):
...         print "From init ... "
... 
>>> a = A()
From init ... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no __call__ method
>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call ... "
... 
>>> b = B()
From init ... 
>>> b()
From call ... 
>>> 

回答 5

__init__将被视为构造函数,其中as __call__方法可以多次用对象调用。这两个__init____call__功能做采取默认参数。

__init__ would be treated as Constructor where as __call__ methods can be called with objects any number of times. Both __init__ and __call__ functions do take default arguments.


回答 6

我将尝试通过一个示例来说明这一点,假设您要打印斐波那契数列中的固定数量的术语。请记住,斐波那契数列的前2个项是1。例如:1、1、2、3、5、8、13 …

您希望包含斐波那契数字的列表仅被初始化一次,然后更新。现在我们可以使用该__call__功能。阅读@mudit verma的答案。就像您希望该对象可作为一个函数来调用,而不是在每次调用时都重新初始化。

例如:

class Recorder:
    def __init__(self):
        self._weights = []
        for i in range(0, 2):
            self._weights.append(1)
        print self._weights[-1]
        print self._weights[-2]
        print "no. above is from __init__"

    def __call__(self, t):
        self._weights = [self._weights[-1], self._weights[-1] + self._weights[-2]]
        print self._weights[-1]
        print "no. above is from __call__"

weight_recorder = Recorder()
for i in range(0, 10):
    weight_recorder(i)

输出为:

1
1
no. above is from __init__
2
no. above is from __call__
3
no. above is from __call__
5
no. above is from __call__
8
no. above is from __call__
13
no. above is from __call__
21
no. above is from __call__
34
no. above is from __call__
55
no. above is from __call__
89
no. above is from __call__
144
no. above is from __call__

如果观察到__init__在第一次实例化该类时仅一次调用了输出,则稍后调用该对象而无需重新初始化。

I will try to explain this using an example, suppose you wanted to print a fixed number of terms from fibonacci series. Remember that the first 2 terms of fibonacci series are 1s. Eg: 1, 1, 2, 3, 5, 8, 13….

You want the list containing the fibonacci numbers to be initialized only once and after that it should update. Now we can use the __call__ functionality. Read @mudit verma’s answer. It’s like you want the object to be callable as a function but not re-initialized every time you call it.

Eg:

class Recorder:
    def __init__(self):
        self._weights = []
        for i in range(0, 2):
            self._weights.append(1)
        print self._weights[-1]
        print self._weights[-2]
        print "no. above is from __init__"

    def __call__(self, t):
        self._weights = [self._weights[-1], self._weights[-1] + self._weights[-2]]
        print self._weights[-1]
        print "no. above is from __call__"

weight_recorder = Recorder()
for i in range(0, 10):
    weight_recorder(i)

The output is:

1
1
no. above is from __init__
2
no. above is from __call__
3
no. above is from __call__
5
no. above is from __call__
8
no. above is from __call__
13
no. above is from __call__
21
no. above is from __call__
34
no. above is from __call__
55
no. above is from __call__
89
no. above is from __call__
144
no. above is from __call__

If you observe the output __init__ was called only one time that’s when the class was instantiated for the first time, later on the object was being called without re-initializing.


回答 7

您也可以使用__call__方法来实现装饰器

本示例摘自Python 3 Patterns,Recipes和Idioms

class decorator_without_arguments(object):
    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print("Inside __init__()")
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print("Inside __call__()")
        self.f(*args)
        print("After self.f( * args)")


@decorator_without_arguments
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)


print("After decoration")
print("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print("After first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print("After second sayHello() call")

输出

You can also use __call__ method in favor of implementing decorators.

This example taken from Python 3 Patterns, Recipes and Idioms

class decorator_without_arguments(object):
    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print("Inside __init__()")
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print("Inside __call__()")
        self.f(*args)
        print("After self.f( * args)")


@decorator_without_arguments
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)


print("After decoration")
print("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print("After first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print("After second sayHello() call")

Output:


回答 8

因此,__init__在创建任何类的实例并初始化实例变量时也会调用。

例:

class User:

    def __init__(self,first_n,last_n,age):
        self.first_n = first_n
        self.last_n = last_n
        self.age = age

user1 = User("Jhone","Wrick","40")

__call__当你调用像任何其他函数的对象被调用。

例:

class USER:
    def __call__(self,arg):
        "todo here"
         print(f"I am in __call__ with arg : {arg} ")


user1=USER()
user1("One") #calling the object user1 and that's gonna call __call__ dunder functions

So, __init__ is called when you are creating an instance of any class and initializing the instance variable also.

Example:

class User:

    def __init__(self,first_n,last_n,age):
        self.first_n = first_n
        self.last_n = last_n
        self.age = age

user1 = User("Jhone","Wrick","40")

And __call__ is called when you call the object like any other function.

Example:

class USER:
    def __call__(self,arg):
        "todo here"
         print(f"I am in __call__ with arg : {arg} ")


user1=USER()
user1("One") #calling the object user1 and that's gonna call __call__ dunder functions

回答 9

__init__是Python类中的一种特殊方法,它是类的构造方法。每当构造该类的对象时,就可以调用它,或者可以说它初始化了一个新对象。例:

    In [4]: class A:
   ...:     def __init__(self, a):
   ...:         print(a)
   ...:
   ...: a = A(10) # An argument is necessary
10

如果我们使用A(),它将给出一个错误 TypeError: __init__() missing 1 required positional argument: 'a'a由于,它将需要1个参数__init__

……..

__call__ 在Class中实现时,可帮助我们将Class实例作为函数调用来调用。

例:

In [6]: class B:
   ...:     def __call__(self,b):
   ...:         print(b)
   ...:
   ...: b = B() # Note we didn't pass any arguments here
   ...: b(20)   # Argument passed when the object is called
   ...:
20

在这里,如果我们使用B(),它就可以正常运行,因为这里没有__init__函数。

__init__ is a special method in Python classes, it is the constructor method for a class. It is called whenever an object of the class is constructed or we can say it initialises a new object. Example:

    In [4]: class A:
   ...:     def __init__(self, a):
   ...:         print(a)
   ...:
   ...: a = A(10) # An argument is necessary
10

If we use A(), it will give an error TypeError: __init__() missing 1 required positional argument: 'a' as it requires 1 argument a because of __init__ .

……..

__call__ when implemented in the Class helps us invoke the Class instance as a function call.

Example:

In [6]: class B:
   ...:     def __call__(self,b):
   ...:         print(b)
   ...:
   ...: b = B() # Note we didn't pass any arguments here
   ...: b(20)   # Argument passed when the object is called
   ...:
20

Here if we use B(), it runs just fine because it doesn’t have an __init__ function here.


回答 10

__call__允许返回任意值,而__init__作为构造函数则隐式返回类的实例。正如其他答案正确指出的那样,__init__它仅被调用一次,而__call__如果已初始化的实例被分配给中间变量,则可以多次调用。

>>> class Test:
...     def __init__(self):
...         return 'Hello'
... 
>>> Test()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: __init__() should return None, not 'str'
>>> class Test2:
...     def __call__(self):
...         return 'Hello'
... 
>>> Test2()()
'Hello'
>>> 
>>> Test2()()
'Hello'
>>> 

__call__ allows to return arbitrary values, while __init__ being an constructor returns the instance of class implicitly. As other answers properly pointed out, __init__ is called just once, while it’s possible to call __call__ multiple times, in case the initialized instance is assigned to intermediate variable.

>>> class Test:
...     def __init__(self):
...         return 'Hello'
... 
>>> Test()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: __init__() should return None, not 'str'
>>> class Test2:
...     def __call__(self):
...         return 'Hello'
... 
>>> Test2()()
'Hello'
>>> 
>>> Test2()()
'Hello'
>>> 

回答 11

上面已经提供了简短的答案。与Java相比,我想提供一些实际的实现。

 class test(object):
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
        def __call__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c


    instance1 = test(1, 2, 3)
    print(instance1.a) #prints 1

    #scenario 1
    #creating new instance instance1
    #instance1 = test(13, 3, 4)
    #print(instance1.a) #prints 13


    #scenario 2
    #modifying the already created instance **instance1**
    instance1(13,3,4)
    print(instance1.a)#prints 13

注意:场景1和场景2在结果输出方面似乎相同。但是在方案1中,我们再次创建另一个新实例instance1。在方案2中,我们只需修改已创建的instance1即可__call__这是有益的,因为系统不需要创建新实例。

在Java中等效

public class Test {

    public static void main(String[] args) {
        Test.TestInnerClass testInnerClass = new Test(). new TestInnerClass(1, 2, 3);
        System.out.println(testInnerClass.a);

        //creating new instance **testInnerClass**
        testInnerClass = new Test().new TestInnerClass(13, 3, 4);
        System.out.println(testInnerClass.a);

        //modifying already created instance **testInnerClass**
        testInnerClass.a = 5;
        testInnerClass.b = 14;
        testInnerClass.c = 23;

        //in python, above three lines is done by testInnerClass(5, 14, 23). For this, we must define __call__ method

    }

    class TestInnerClass /* non-static inner class */{

        private int a, b,c;

        TestInnerClass(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }
}

Short and sweet answers are already provided above. I wanna provide some practical implementation as compared with Java.

 class test(object):
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
        def __call__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c


    instance1 = test(1, 2, 3)
    print(instance1.a) #prints 1

    #scenario 1
    #creating new instance instance1
    #instance1 = test(13, 3, 4)
    #print(instance1.a) #prints 13


    #scenario 2
    #modifying the already created instance **instance1**
    instance1(13,3,4)
    print(instance1.a)#prints 13

Note: scenario 1 and scenario 2 seems same in terms of result output. But in scenario1, we again create another new instance instance1. In scenario2, we simply modify already created instance1. __call__ is beneficial here as the system doesn’t need to create new instance.

Equivalent in Java

public class Test {

    public static void main(String[] args) {
        Test.TestInnerClass testInnerClass = new Test(). new TestInnerClass(1, 2, 3);
        System.out.println(testInnerClass.a);

        //creating new instance **testInnerClass**
        testInnerClass = new Test().new TestInnerClass(13, 3, 4);
        System.out.println(testInnerClass.a);

        //modifying already created instance **testInnerClass**
        testInnerClass.a = 5;
        testInnerClass.b = 14;
        testInnerClass.c = 23;

        //in python, above three lines is done by testInnerClass(5, 14, 23). For this, we must define __call__ method

    }

    class TestInnerClass /* non-static inner class */{

        private int a, b,c;

        TestInnerClass(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }
}

回答 12

我们可以使用call方法将其他类方法用作静态方法。

class _Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

class Model:

    def get_instance(conn, table_name):

        """ do something"""

    get_instance = _Callable(get_instance)

provs_fac = Model.get_instance(connection, "users")  

We can use call method to use other class methods as static methods.

class _Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

class Model:

    def get_instance(conn, table_name):

        """ do something"""

    get_instance = _Callable(get_instance)

provs_fac = Model.get_instance(connection, "users")  

什么是mixin,为什么它们有用?

问题:什么是mixin,为什么它们有用?

在“ Python编程 ”中,Mark Lutz提到了“ mixins”。我来自C / C ++ / C#背景,以前没有听说过这个词。什么是mixin?

本示例的两行之间进行阅读(我已经链接到它,因为它很长),我认为这是使用多重继承来扩展类而不是“适当的”子类的一种情况。这是正确的吗?

为什么我要这样做而不是将新功能放入子类中?因此,为什么混合/多重继承方法比使用组合更好?

什么将mixin与多重继承分开?这仅仅是语义问题吗?

In “Programming Python“, Mark Lutz mentions “mixins”. I’m from a C/C++/C# background and I have not heard the term before. What is a mixin?

Reading between the lines of this example (which I’ve linked to because it’s quite long), I’m presuming it’s a case of using multiple inheritance to extend a class as opposed to ‘proper’ subclassing. Is this right?

Why would I want to do that rather than put the new functionality into a subclass? For that matter, why would a mixin/multiple inheritance approach be better than using composition?

What separates a mixin from multiple inheritance? Is it just a matter of semantics?


回答 0

mixin是一种特殊的多重继承。使用mixin的主要情况有两种:

  1. 您想为一个类提供很多可选功能。
  2. 您想在许多不同的类中使用一种特定功能。

例如,请考虑werkzeug的请求和响应系统。我可以说一个普通的旧请求对象:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

如果我想添加接受标头支持,我会做

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

如果我想创建一个支持接受标头,etag,身份验证和用户代理支持的请求对象,则可以执行以下操作:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

区别是细微的,但是在上面的示例中,mixin类并不是独立存在的。在更传统的多重继承中,AuthenticationMixin(例如)可能更像Authenticator。也就是说,该类可能会设计为独立存在。

A mixin is a special kind of multiple inheritance. There are two main situations where mixins are used:

  1. You want to provide a lot of optional features for a class.
  2. You want to use one particular feature in a lot of different classes.

For an example of number one, consider werkzeug’s request and response system. I can make a plain old request object by saying:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

If I want to add accept header support, I would make that

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

If I wanted to make a request object that supports accept headers, etags, authentication, and user agent support, I could do this:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
    pass

The difference is subtle, but in the above examples, the mixin classes weren’t made to stand on their own. In more traditional multiple inheritance, the AuthenticationMixin (for example) would probably be something more like Authenticator. That is, the class would probably be designed to stand on its own.


回答 1

首先,您应该注意,mixin仅存在于多种继承语言中。您不能使用Java或C#进行混合。

基本上,mixin是独立的基本类型,可为子类提供有限的功能和多态共振。如果您正在考虑使用C#,请考虑一下您不必实际实现的接口,因为该接口已经实现了。您只需继承它并从其功能中受益。

Mixins通常范围狭窄,不打算扩展。

[编辑-关于原因:]

既然您问过,我想我应该说一下原因。最大的好处是您不必一遍又一遍地自己做。在C#中,mixin受益最大的地方可能是Disposal模式。每当实现IDisposable时,几乎总是希望遵循相同的模式,但最终会以较小的变化编写和重新编写相同的基本代码。如果有可扩展的Disposal mixin,则可以节省很多额外的键入操作。

[编辑2-回答您的其他问题]

什么将mixin与多重继承分开?这仅仅是语义问题吗?

是。mixin和标准多重继承之间的区别只是语义问题。具有多重继承的类可能会使用混合作为多重继承的一部分。

mixin的目的是创建一个可以通过继承“混合”到任何其他类型的类型,而不会影响继承类型,同时仍然为该类型提供一些有益的功能。

再次考虑一下已经实现的接口。

我个人不使用mixins,因为我主要使用不支持它们的语言进行开发,因此我很难拿出一个像样的示例来提供“啊!”的好例子。你的时刻。但我会再试一次。我将使用一个人为设计的示例-大多数语言已经以某种方式提供了该功能-希望这将解释应该如何创建和使用mixin。开始:

假设您具有一个可以与XML进行序列化的类型。您希望该类型提供“ ToXML”方法,该方法返回包含具有该类型的数据值的XML片段的字符串,以及“ FromXML”,其允许该类型从字符串中的XML片段重建其数据值。同样,这是一个人为的示例,因此也许您使用文件流或语言运行时库中的XML Writer类…等等。关键是您想将对象序列化为XML并从XML取回新对象。

此示例中的另一个重要点是您希望以通用方式执行此操作。您不需要为要序列化的每种类型实现“ ToXML”和“ FromXML”方法,而是需要一些通用的方法来确保您的类型可以做到这一点并且可以正常工作。您想要代码重用。

如果您的语言支持,则可以创建XmlSerializable mixin为您完成工作。此类型将实现ToXML和FromXML方法。它将使用对示例不重要的某种机制,能够从与之混合的任何类型中收集所有必要的数据,以构建ToXML返回的XML片段,并且当FromXML为叫。

和..就是这样。要使用它,您需要将任何类型的类型都需要序列化为XML,才能从XmlSerializable继承。每当需要序列化或反序列化该类型时,只需调用ToXML或FromXML。实际上,由于XmlSerializable是完全成熟的类型并且是多态的,因此可以想象到,您可以构建一个对原始类型一无所知的文档序列化器,只接受一个XmlSerializable类型的数组。

现在想象一下将此场景用于其他用途,例如创建一个确保每个混合了它的类的mixin记录每个方法调用,或者一个为混合它的类型提供事务性的mixin。列表可以继续。

如果您只是将mixin视为旨在为类型添加少量功能而又不影响该类型的小型基本类型,那么您就是无所不能。

希望。:)

First, you should note that mixins only exist in multiple-inheritance languages. You can’t do a mixin in Java or C#.

Basically, a mixin is a stand-alone base type that provides limited functionality and polymorphic resonance for a child class. If you’re thinking in C#, think of an interface that you don’t have to actually implement because it’s already implemented; you just inherit from it and benefit from its functionality.

Mixins are typically narrow in scope and not meant to be extended.

[edit — as to why:]

I suppose I should address why, since you asked. The big benefit is that you don’t have to do it yourself over and over again. In C#, the biggest place where a mixin could benefit might be from the Disposal pattern. Whenever you implement IDisposable, you almost always want to follow the same pattern, but you end up writing and re-writing the same basic code with minor variations. If there were an extendable Disposal mixin, you could save yourself a lot of extra typing.

[edit 2 — to answer your other questions]

What separates a mixin from multiple inheritance? Is it just a matter of semantics?

Yes. The difference between a mixin and standard multiple inheritance is just a matter of semantics; a class that has multiple inheritance might utilize a mixin as part of that multiple inheritance.

The point of a mixin is to create a type that can be “mixed in” to any other type via inheritance without affecting the inheriting type while still offering some beneficial functionality for that type.

Again, think of an interface that is already implemented.

I personally don’t use mixins since I develop primarily in a language that doesn’t support them, so I’m having a really difficult time coming up with a decent example that will just supply that “ahah!” moment for you. But I’ll try again. I’m going to use an example that’s contrived — most languages already provide the feature in some way or another — but that will, hopefully, explain how mixins are supposed to be created and used. Here goes:

Suppose you have a type that you want to be able to serialize to and from XML. You want the type to provide a “ToXML” method that returns a string containing an XML fragment with the data values of the type, and a “FromXML” that allows the type to reconstruct its data values from an XML fragment in a string. Again, this is a contrived example, so perhaps you use a file stream, or an XML Writer class from your language’s runtime library… whatever. The point is that you want to serialize your object to XML and get a new object back from XML.

The other important point in this example is that you want to do this in a generic way. You don’t want to have to implement a “ToXML” and “FromXML” method for every type that you want to serialize, you want some generic means of ensuring that your type will do this and it just works. You want code reuse.

If your language supported it, you could create the XmlSerializable mixin to do your work for you. This type would implement the ToXML and the FromXML methods. It would, using some mechanism that’s not important to the example, be capable of gathering all the necessary data from any type that it’s mixed in with to build the XML fragment returned by ToXML and it would be equally capable of restoring that data when FromXML is called.

And.. that’s it. To use it, you would have any type that needs to be serialized to XML inherit from XmlSerializable. Whenever you needed to serialize or deserialize that type, you would simply call ToXML or FromXML. In fact, since XmlSerializable is a fully-fledged type and polymorphic, you could conceivably build a document serializer that doesn’t know anything about your original type, accepting only, say, an array of XmlSerializable types.

Now imagine using this scenario for other things, like creating a mixin that ensures that every class that mixes it in logs every method call, or a mixin that provides transactionality to the type that mixes it in. The list can go on and on.

If you just think of a mixin as a small base type designed to add a small amount of functionality to a type without otherwise affecting that type, then you’re golden.

Hopefully. :)


回答 2

该答案旨在通过以下示例解释mixin :

  • 自包含:简短,无需了解任何库即可理解示例。

  • 用Python而不是其他语言。

    可以理解,存在其他语言(例如Ruby)的示例,因为该术语在这些语言中更为常见,但这是Python线程。

它还应考虑有争议的问题:

是否需要多重继承来表征mixin?

定义

我还没有看到来自“权威”来源的引文,清楚地说明了Python中的mixin。

我已经看到了mixin的2种可能定义(如果认为它们与其他类似概念(例如抽象基类)不同),人们并不完全同意哪种正确。

不同语言之间的共识可能会有所不同。

定义1:无多重继承

mixin是一个类,以便该类的某些方法使用该类中未定义的方法。

因此,该类不是要实例化的,而应用作基类。否则,该实例将具有在不引发异常的情况下无法调用的方法。

一些资料来源增加的一个约束是该类可能不包含数据,仅包含方法,但我不明白为什么这样做是必要的。但是实际上,许多有用的mixin没有任何数据,并且没有数据的基类更易于使用。

一个典型的例子是从only <=和实现所有比较运算符==

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

这个特定的例子可以通过functools.total_ordering()装饰器来实现,但是这里的游戏是重新发明轮子:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

定义2:多重继承

mixin是一种设计模式,其中基类的某些方法使用其未定义的方法,并且该方法应由另一个基类实现,而不是由定义1中的派生方法实现。

术语“ 混合类”是指打算在该设计模式中使用的基类(使用方法的那些类是TODO,还是实现该方法的那些?

决定给定类是否为混合类并不容易:该方法可以仅在派生类上实现,在这种情况下,我们回到定义1。您必须考虑作者的意图。

这种模式很有趣,因为可以通过选择不同的基类来重组功能:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

权威的Python事件

collections.abc官方文档中,该文档明确使用术语Mixin Methods

它指出如果一个类:

  • 贯彻 __next__
  • 从单个类继承 Iterator

然后该类将免费获得一个__iter__ mixin方法

因此,至少在文档的这一点上,mixin不需要多重继承,并且与定义1保持一致。

当然,文档在不同点上可能是矛盾的,并且其他重要的Python库可能正在其文档中使用其他定义。

该页面还使用术语Set mixin,它明确表明类似类Set并且Iterator可以称为Mixin类。

用其他语言

  • 红宝石:显然不需要混入多重继承,如主要参考书如提到的编程的Ruby和Ruby编程语言

  • C ++:未实现的方法是纯虚拟方法。

    定义1与抽象类(具有纯虚方法的类)的定义一致。该类无法实例化。

    虚拟继承可以定义2:来自两个派生类的多重继承

This answer aims to explain mixins with examples that are:

  • self-contained: short, with no need to know any libraries to understand the example.

  • in Python, not in other languages.

    It is understandable that there were examples from other languages such as Ruby since the term is much more common in those languages, but this is a Python thread.

It shall also consider the controversial question:

Is multiple inheritance necessary or not to characterize a mixin?

Definitions

I have yet to see a citation from an “authoritative” source clearly saying what is a mixin in Python.

I have seen 2 possible definitions of a mixin (if they are to be considered as different from other similar concepts such as abstract base classes), and people don’t entirely agree on which one is correct.

The consensus may vary between different languages.

Definition 1: no multiple inheritance

A mixin is a class such that some method of the class uses a method which is not defined in the class.

Therefore the class is not meant to be instantiated, but rather serve as a base class. Otherwise the instance would have methods that cannot be called without raising an exception.

A constraint which some sources add is that the class may not contain data, only methods, but I don’t see why this is necessary. In practice however, many useful mixins don’t have any data, and base classes without data are simpler to use.

A classic example is the implementation of all comparison operators from only <= and ==:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

This particular example could have been achieved via the functools.total_ordering() decorator, but the game here was to reinvent the wheel:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Definition 2: multiple inheritance

A mixin is a design pattern in which some method of a base class uses a method it does not define, and that method is meant to be implemented by another base class, not by the derived like in Definition 1.

The term mixin class refers to base classes which are intended to be used in that design pattern (TODO those that use the method, or those that implement it?)

It is not easy to decide if a given class is a mixin or not: the method could be just implemented on the derived class, in which case we’re back to Definition 1. You have to consider the author’s intentions.

This pattern is interesting because it is possible to recombine functionalities with different choices of base classes:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Authoritative Python occurrences

At the official documentatiton for collections.abc the documentation explicitly uses the term Mixin Methods.

It states that if a class:

  • implements __next__
  • inherits from a single class Iterator

then the class gets an __iter__ mixin method for free.

Therefore at least on this point of the documentation, mixin does not not require multiple inheritance, and is coherent with Definition 1.

The documentation could of course be contradictory at different points, and other important Python libraries might be using the other definition in their documentation.

This page also uses the term Set mixin, which clearly suggests that classes like Set and Iterator can be called Mixin classes.

In other languages

  • Ruby: Clearly does not require multiple inheritance for mixin, as mentioned in major reference books such as Programming Ruby and The Ruby programming Language

  • C++: A method that is not implemented is a pure virtual method.

    Definition 1 coincides with the definition of an abstract class (a class that has a pure virtual method). That class cannot be instantiated.

    Definition 2 is possible with virtual inheritance: Multiple Inheritance from two derived classes


回答 3

我认为它们是使用多重继承的一种有条理的方式-因为mixin最终只是(可能)遵循关于被称为mixin的类的约定的另一个python类。

我对管理您称为Mixin的约定的理解是Mixin:

  • 添加方法但不添加实例变量(类常量可以)
  • 仅继承自object(在Python中)

这样,它限制了多重继承的潜在复杂性,并且通过限制外观(相对于完全多重继承),使跟踪程序流变得相当容易。它们类似于ruby模块

如果我想添加实例变量(比单继承具有更大的灵活性),那么我倾向于组合。

话虽如此,我看到了名为XYZMixin的类,它们确实具有实例变量。

I think of them as a disciplined way of using multiple inheritance – because ultimately a mixin is just another python class that (might) follow the conventions about classes that are called mixins.

My understanding of the conventions that govern something you would call a Mixin are that a Mixin:

  • adds methods but not instance variables (class constants are OK)
  • only inherits from object (in Python)

That way it limits the potential complexity of multiple inheritance, and makes it reasonably easy to track the flow of your program by limiting where you have to look (compared to full multiple inheritance). They are similar to ruby modules.

If I want to add instance variables (with more flexibility than allowed for by single inheritance) then I tend to go for composition.

Having said that, I have seen classes called XYZMixin that do have instance variables.


回答 4

Mixins是“编程”中的一个概念,其中该类提供功能,但并不用于实例化。Mixins的主要目的是提供独立的功能,并且最好的是,mixin本身不与其他mixin继承并且也避免状态。在诸如Ruby之类的语言中,有一些直接的语言支持,但对于Python则没有。但是,您可以使用多类继承来执行Python中提供的功能。

我观看了http://www.youtube.com/watch?v=v_uKI2NOLEM的视频,以了解Mixins的基础知识。对于初学者来说,了解mixin的基础知识,它们如何工作以及在实现它们时可能遇到的问题非常有用。

维基百科仍然是最好的:http : //en.wikipedia.org/wiki/Mixin

Mixins is a concept in Programming in which the class provides functionalities but it is not meant to be used for instantiation. Main purpose of Mixins is to provide functionalities which are standalone and it would be best if the mixins itself do not have inheritance with other mixins and also avoid state. In languages such as Ruby, there is some direct language support but for Python, there isn’t. However, you could used multi-class inheritance to execute the functionality provided in Python.

I watched this video http://www.youtube.com/watch?v=v_uKI2NOLEM to understand the basics of mixins. It is quite useful for a beginner to understand the basics of mixins and how they work and the problems you might face in implementing them.

Wikipedia is still the best: http://en.wikipedia.org/wiki/Mixin


回答 5

什么将mixin与多重继承分开?这仅仅是语义问题吗?

混合是多重继承的有限形式。在某些语言中,将mixin添加到类的机制(在语法方面)与继承略有不同。

特别是在Python的上下文中,mixin是一个父类,它为子类提供功能,但本身并不打算实例化。

您可能会说,“那只是多重继承,而不是真正的mixin”是因为实际上可以实例化和使用对于mixin感到困惑的类,因此,这确实是语义上的,而且非常真实。

多重继承的例子

该示例来自文档,是OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

它从模块子类化Counter和。OrderedDictcollections

双方CounterOrderedDict意图被实例化,并在自己使用。但是,通过将它们都子类化,我们可以得到一个有序的计数器,并在每个对象中重用代码。

这是重用代码的有效方法,但也可能会出现问题。如果事实证明其中一个对象中存在错误,则不小心修复它可能会在子类中创建错误。

混合的例子

通常将Mixins提倡为获得代码重用的方式,而又避免了诸如OrderedCounter之类的协作多重继承可能存在的潜在耦合问题。当您使用mixins时,您使用的功能与数据紧密耦合。

与上面的示例不同,mixin不能单独使用。它提供了新的或不同的功能。

例如,标准库有一对夫妇在混入socketserver

可以使用这些混合类来创建每种类型服务器的分支和线程版本。例如,ThreadingUDPServer的创建如下:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

混合类首先出现,因为它会覆盖UDPServer中定义的方法。设置各种属性还可以更改基础服务器机制的行为。

在这种情况下,mixin方法将覆盖UDPServer对象定义中的方法以允许并发。

覆盖的方法似乎是process_request,它还提供了另一种方法process_request_thread。这是源代码

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

人为的例子

这是一个mixin,主要用于演示目的-大多数对象的发展将超出此repr的用途:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

用法是:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

和用法:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

What separates a mixin from multiple inheritance? Is it just a matter of semantics?

A mixin is a limited form of multiple inheritance. In some languages the mechanism for adding a mixin to a class is slightly different (in terms of syntax) from that of inheritance.

In the context of Python especially, a mixin is a parent class that provides functionality to subclasses but is not intended to be instantiated itself.

What might cause you to say, “that’s just multiple inheritance, not really a mixin” is if the class that might be confused for a mixin can actually be instantiated and used – so indeed it is a semantic, and very real, difference.

Example of Multiple Inheritance

This example, from the documentation, is an OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

It subclasses both the Counter and the OrderedDict from the collections module.

Both Counter and OrderedDict are intended to be instantiated and used on their own. However, by subclassing them both, we can have a counter that is ordered and reuses the code in each object.

This is a powerful way to reuse code, but it can also be problematic. If it turns out there’s a bug in one of the objects, fixing it without care could create a bug in the subclass.

Example of a Mixin

Mixins are usually promoted as the way to get code reuse without potential coupling issues that cooperative multiple inheritance, like the OrderedCounter, could have. When you use mixins, you use functionality that isn’t as tightly coupled to the data.

Unlike the example above, a mixin is not intended to be used on its own. It provides new or different functionality.

For example, the standard library has a couple of mixins in the socketserver library.

Forking and threading versions of each type of server can be created using these mix-in classes. For instance, ThreadingUDPServer is created as follows:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

The mix-in class comes first, since it overrides a method defined in UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.

In this case, the mixin methods override the methods in the UDPServer object definition to allow for concurrency.

The overridden method appears to be process_request and it also provides another method, process_request_thread. Here it is from the source code:

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

A Contrived Example

This is a mixin that is mostly for demonstration purposes – most objects will evolve beyond the usefulness of this repr:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

and usage would be:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

And usage:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

回答 6

我认为这里有一些很好的解释,但我想提供另一种观点。

在Scala中,您可以执行混合操作,如此处所述,但非常有趣的是,混合操作实际上是“融合”在一起的,以创建一种新的继承类。本质上,您不是从多个类/ mixins继承,而是生成一种具有mixin所有属性的新类。这是有道理的,因为Scala基于JVM(目前不支持多重继承)(从Java 8开始)。顺便说一下,这种mixin类类型是一种特殊类型,在Scala中称为Trait。

它以类定义的方式提示:类NewClass扩展FirstMixin和SecondMixin以及ThirdMixin …

我不确定CPython解释器是否执行相同的操作(mixin类组成),但是我不会感到惊讶。同样,来自C ++背景,我不会将ABC或“接口”等同于mixin,它是一个相似的概念,但是在使用和实现上存在分歧。

I think there have been some good explanations here but I wanted to provide another perspective.

In Scala, you can do mixins as has been described here but what is very interesting is that the mixins are actually ‘fused’ together to create a new kind of class to inherit from. In essence, you do not inherit from multiple classes/mixins, but rather, generate a new kind of class with all the properties of the mixin to inherit from. This makes sense since Scala is based on the JVM where multiple-inheritance is not currently supported (as of Java 8). This mixin class type, by the way, is a special type called a Trait in Scala.

It’s hinted at in the way a class is defined: class NewClass extends FirstMixin with SecondMixin with ThirdMixin …

I’m not sure if the CPython interpreter does the same (mixin class-composition) but I wouldn’t be surprised. Also, coming from a C++ background, I would not call an ABC or ‘interface’ equivalent to a mixin — it’s a similar concept but divergent in use and implementation.


回答 7

我建议不要在新的Python代码中进行混入,如果您能找到其他解决方法(例如,代替合成的继承关系,或者只是在自己的类中使用Monkey修补方法),那就不多了努力。

在老式类中,您可以使用混入作为从另一个类中获取一些方法的一种方式。但是在新式世界中,即使是混入,一切也都继承自object。这意味着对多重继承的任何使用自然会引入MRO问题

有多种方法可以使多继承MRO在Python中工作,其中最著名的是super()函数,但这意味着您必须使用super()来完成整个类的层次结构,并且要理解控制流要困难得多。

I’d advise against mix-ins in new Python code, if you can find any other way around it (such as composition-instead-of-inheritance, or just monkey-patching methods into your own classes) that isn’t much more effort.

In old-style classes you could use mix-ins as a way of grabbing a few methods from another class. But in the new-style world everything, even the mix-in, inherits from object. That means that any use of multiple inheritance naturally introduces MRO issues.

There are ways to make multiple-inheritance MRO work in Python, most notably the super() function, but it means you have to do your whole class hierarchy using super(), and it’s considerably more difficult to understand the flow of control.


回答 8

也许有几个例子会有所帮助。

如果您要构建一个类并希望它像字典一样工作,则可以定义所有__ __必要的方法。但这有点痛苦。作为替代方案,您可以只定义一些,并从UserDict.DictMixin(继承自collections.DictMixinpy3k中)继承(除了任何其他继承)。这将具有自动定义其余所有字典api的作用。

第二个示例:GUI工具箱wxPython允许您创建具有多列的列表控件(例如Windows资源管理器中的文件显示)。默认情况下,这些列表是非常基本的。您可以添加其他功能,例如通过单击列标题,从ListCtrl继承并添加适当的mixins来按特定列对列表进行排序的功能。

Perhaps a couple of examples will help.

If you’re building a class and you want it to act like a dictionary, you can define all the various __ __ methods necessary. But that’s a bit of a pain. As an alternative, you can just define a few, and inherit (in addition to any other inheritance) from UserDict.DictMixin (moved to collections.DictMixin in py3k). This will have the effect of automatically defining all the rest of the dictionary api.

A second example: the GUI toolkit wxPython allows you to make list controls with multiple columns (like, say, the file display in Windows Explorer). By default, these lists are fairly basic. You can add additional functionality, such as the ability to sort the list by a particular column by clicking on the column header, by inheriting from ListCtrl and adding appropriate mixins.


回答 9

这不是Python的示例,但在D编程语言中,该术语mixin用于指代使用几乎相同方式的构造。在课堂上添加一堆东西。

在D中(顺便说一下,它不执行MI),这是通过将一个模板(认为具有语法意识和安全的宏,您将接近)插入一个范围来完成的。这允许在类,结构,函数,模块或任何可以扩展为任意数量的声明的代码中使用一行代码。

It’s not a Python example but in the D programing language the term mixin is used to refer to a construct used much the same way; adding a pile of stuff to a class.

In D (which by the way doesn’t do MI) this is done by inserting a template (think syntactically aware and safe macros and you will be close) into a scope. This allows for a single line of code in a class, struct, function, module or whatever to expand to any number of declarations.


回答 10

OP提到他/她从未听说过C ++中的mixin,也许是因为它们在C ++中被称为“好奇重复模板模式(CRTP)”。另外,@ Ciro Santilli提到mixin是通过C ++中的抽象基类实现的。尽管可以使用抽象基类来实现mixin,但这是一个过高的选择,因为可以在编译时使用模板在运行时实现虚拟功能的功能,而无需在运行时查找虚拟表的开销。

此处详细描述了CRTP模式

我已经使用以下模板类将@Ciro Santilli的答案中的python示例转换为C ++:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

编辑:在ComparableMixin中添加了受保护的构造函数,因此它只能被继承而不能被实例化。更新了示例,以显示创建ComparableMixin对象时受保护的构造函数将如何导致编译错误。

OP mentioned that he/she never heard of mixin in C++, perhaps that is because they are called Curiously Recurring Template Pattern (CRTP) in C++. Also, @Ciro Santilli mentioned that mixin is implemented via abstract base class in C++. While abstract base class can be used to implement mixin, it is an overkill as the functionality of virtual function at run-time can be achieved using template at compile time without the overhead of virtual table lookup at run-time.

The CRTP pattern is described in detail here

I have converted the python example in @Ciro Santilli’s answer into C++ using template class below:

    #include <iostream>
    #include <assert.h>

    template <class T>
    class ComparableMixin {
    public:
        bool operator !=(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) == static_cast<T&>(other));
        }
        bool operator <(ComparableMixin &other) {
            return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
        }
        bool operator >(ComparableMixin &other) {
            return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
        }
        bool operator >=(ComparableMixin &other) {
            return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
        }
        protected:
            ComparableMixin() {}
    };

    class Integer: public ComparableMixin<Integer> {
    public:
     Integer(int i) {
         this->i = i;
     }
     int i;
     bool operator <=(Integer &other) {
         return (this->i <= other.i);
     }
     bool operator ==(Integer &other) {
         return (this->i == other.i);
     }
    };

int main() {

    Integer i(0) ;
    Integer j(1) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

EDIT: Added protected constructor in ComparableMixin so that it can only be inherited and not instantiated. Updated the example to show how protected constructor will cause compilation error when an object of ComparableMixin is created.


回答 11

也许来自ruby的示例可以帮助您:

您可以包括mixin Comparable并定义一个功能"<=>(other)",mixin提供所有这些功能:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

它通过调用<=>(other)并返回正确的结果来做到这一点。

"instance <=> other"返回0,如果两个对象是相等的,小于0,如果instance是比更大other和超过0,如果other是更大的。

Maybe an example from ruby can help:

You can include the mixin Comparable and define one function "<=>(other)", the mixin provides all those functions:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

It does this by invoking <=>(other) and giving back the right result.

"instance <=> other" returns 0 if both objects are equal, less than 0 if instance is bigger than other and more than 0 if other is bigger.


回答 12

mixin提供了一种在类中添加功能的方法,即,您可以通过将模块包含在所需类中来与模块中定义的方法进行交互。尽管ruby不支持多重继承,但提供了mixin作为实现该目的的替代方法。

这是一个示例,说明如何使用mixin实现多重继承。

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

mixin gives a way to add functionality in a class, i.e you can interact with methods defined in a module by including the module inside the desired class. Though ruby doesn’t supports multiple inheritance but provides mixin as an alternative to achieve that.

here is an example that explains how multiple inheritance is achieved using mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

回答 13

我只是使用python mixin对python milters进行单元测试。通常情况下,军阀会与MTA对话,因此很难进行单元测试。测试混入将覆盖与MTA对话的方法,并创建由测试用例驱动的模拟环境。

因此,您采用未修改的milter应用程序,例如spfmilter和mixin TestBase,如下所示:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

然后,在milter应用程序的测试用例中使用TestMilter:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup

I just used a python mixin to implement unit testing for python milters. Normally, a milter talks to an MTA, making unit testing difficult. The test mixin overrides methods that talk to the MTA, and create a simulated environment driven by test cases instead.

So, you take an unmodified milter application, like spfmilter, and mixin TestBase, like this:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Then, use TestMilter in the test cases for the milter application:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup


回答 14

我认为以前的答复很好地定义了什么是MixIn。但是,为了更好地理解它们,从代码/实现的角度比较MixIn抽象类接口可能是有用的:

1.抽象类

  • 需要包含一个或多个抽象方法

  • 抽象类 可以包含状态(实例变量)和非抽象方法

2.界面

  • 接口包含抽象方法(没有非抽象方法,没有内部状态)

3. MixIns

  • 混音(如接口)包含内部状态(实例变量)
  • 混音包含一个或多个非抽象方法(与接口不同,它们可以包含非抽象方法)

在例如Python中,这些只是约定,因为以上所有内容均定义为classes。但是,抽象类,接口MixIns的共同特征是它们不应独立存在,即不应实例化。

I think previous responses defined very well what MixIns are. However, in order to better understand them, it might be useful to compare MixIns with Abstract Classes and Interfaces from the code/implementation perspective:

1. Abstract Class

  • Class that needs to contain one or more abstract methods

  • Abstract Class can contain state (instance variables) and non-abstract methods

2. Interface

  • Interface contains abstract methods only (no non-abstract methods and no internal state)

3. MixIns

  • MixIns (like Interfaces) do not contain internal state (instance variables)
  • MixIns contain one or more non-abstract methods (they can contain non-abstract methods unlike interfaces)

In e.g. Python these are just conventions, because all of the above are defined as classes. However, the common feature of both Abstract Classes, Interfaces and MixIns is that they should not exist on their own, i.e. should not be instantiated.


回答 15

我读到您有ac#背景。因此,一个好的起点可能是.NET的mixin实现。

您可能想在http://remix.codeplex.com/上检查Codeplex项目。

观看lang.net专题讨论会链接以获取概述。Codeplex页面上的文档还有更多内容。

问候斯蒂芬

I read that you have a c# background. So a good starting point might be a mixin implementation for .NET.

You might want to check out the codeplex project at http://remix.codeplex.com/

Watch the lang.net Symposium link to get an overview. There is still more to come on documentation on codeplex page.

regards Stefan


如何根据对象的属性对对象列表进行排序?

问题:如何根据对象的属性对对象列表进行排序?

我有一个Python对象列表,我想按对象本身的属性对其进行排序。该列表如下所示:

>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
 <Tag: aes>, <Tag: ajax> ...]

每个对象都有一个计数:

>>> ut[1].count
1L

我需要按递减计数对列表进行排序。

我已经看到了几种方法,但是我正在寻找Python的最佳实践。

I’ve got a list of Python objects that I’d like to sort by an attribute of the objects themselves. The list looks like:

>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
 <Tag: aes>, <Tag: ajax> ...]

Each object has a count:

>>> ut[1].count
1L

I need to sort the list by number of counts descending.

I’ve seen several methods for this, but I’m looking for best practice in Python.


回答 0

# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)

# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)

有关按键排序的更多信息。

# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)

# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)

More on sorting by keys.


回答 1

可以使用最快的方法,尤其是在您的列表中有很多记录的情况下operator.attrgetter("count")。但是,它可以在预操作者版本的Python上运行,因此具有后备机制会很好。然后,您可能需要执行以下操作:

try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda

ut.sort(key=keyfun, reverse=True) # sort in-place

A way that can be fastest, especially if your list has a lot of records, is to use operator.attrgetter("count"). However, this might run on an pre-operator version of Python, so it would be nice to have a fallback mechanism. You might want to do the following, then:

try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda

ut.sort(key=keyfun, reverse=True) # sort in-place

回答 2

读者应注意,key =方法:

ut.sort(key=lambda x: x.count, reverse=True)

比向对象添加丰富的比较运算符快许多倍。我很惊讶地阅读了这篇文章(“ Python in a Nutshell”的第485页)。您可以通过在这个小程序上运行测试来确认这一点:

#!/usr/bin/env python
import random

class C:
    def __init__(self,count):
        self.count = count

    def __cmp__(self,other):
        return cmp(self.count,other.count)

longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]

longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs

我的非常少的测试表明,第一种方法的运行速度要慢10倍以上,但书中说,一般而言,它仅慢5倍左右。他们说的原因是由于python(timsort)中使用了高度优化的排序算法。

仍然,.sort(lambda)比普通的旧.sort()快是很奇怪的。我希望他们能解决这个问题。

Readers should notice that the key= method:

ut.sort(key=lambda x: x.count, reverse=True)

is many times faster than adding rich comparison operators to the objects. I was surprised to read this (page 485 of “Python in a Nutshell”). You can confirm this by running tests on this little program:

#!/usr/bin/env python
import random

class C:
    def __init__(self,count):
        self.count = count

    def __cmp__(self,other):
        return cmp(self.count,other.count)

longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]

longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs

My, very minimal, tests show the first sort is more than 10 times slower, but the book says it is only about 5 times slower in general. The reason they say is due to the highly optimizes sort algorithm used in python (timsort).

Still, its very odd that .sort(lambda) is faster than plain old .sort(). I hope they fix that.


回答 3

面向对象的方法

最好将对象排序逻辑(如果适用)设置为类的属性,而不是在每个实例中都要求进行排序。

这样可以确保一致性,并且不需要样板代码。

至少,您应该指定__eq____lt__操作此功能。然后使用sorted(list_of_objects)

class Card(object):

    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit

    def __eq__(self, other):
        return self.rank == other.rank and self.suit == other.suit

    def __lt__(self, other):
        return self.rank < other.rank

hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand]  # [10, 2, 12, 13, 14]

hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted]  # [2, 10, 12, 13, 14]

Object-oriented approach

It’s good practice to make object sorting logic, if applicable, a property of the class rather than incorporated in each instance the ordering is required.

This ensures consistency and removes the need for boilerplate code.

At a minimum, you should specify __eq__ and __lt__ operations for this to work. Then just use sorted(list_of_objects).

class Card(object):

    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit

    def __eq__(self, other):
        return self.rank == other.rank and self.suit == other.suit

    def __lt__(self, other):
        return self.rank < other.rank

hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand]  # [10, 2, 12, 13, 14]

hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted]  # [2, 10, 12, 13, 14]

回答 4

from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)

回答 5

它看起来很像Django ORM模型实例的列表。

为什么不对这样的查询进行排序:

ut = Tag.objects.order_by('-count')

It looks much like a list of Django ORM model instances.

Why not sort them on query like this:

ut = Tag.objects.order_by('-count')

回答 6

将丰富的比较运算符添加到对象类,然后使用列表的sort()方法。
参见python中的丰富比较


更新:尽管此方法可行,但我认为Triptych的解决方案更简单,因此更适合您的情况。

Add rich comparison operators to the object class, then use sort() method of the list.
See rich comparison in python.


Update: Although this method would work, I think solution from Triptych is better suited to your case because way simpler.


回答 7

如果要排序的属性property,则可以避免导入,operator.attrgetter而可以使用属性的fget方法。

例如,对于Circle具有属性的类,radius我们可以circles按如下所示对半径列表进行排序:

result = sorted(circles, key=Circle.radius.fget)

这不是最知名的功能,但通常使我免于导入的麻烦。

If the attribute you want to sort by is a property, then you can avoid importing operator.attrgetter and use the property’s fget method instead.

For example, for a class Circle with a property radius we could sort a list of circles by radii as follows:

result = sorted(circles, key=Circle.radius.fget)

This is not the most well-known feature but often saves me a line with the import.


Python的__init__和self是做什么的?

问题:Python的__init__和self是做什么的?

我正在学习Python编程语言,遇到了一些我不太了解的东西。

用类似的方法:

def method(self, blah):
    def __init__(?):
        ....
    ....

怎么self办?这是什么意思?它是强制性的吗?

__init__方法做什么?为什么有必要?(等等。)

我认为它们可能是OOP构造,但我不太了解。

I’m learning the Python programming language and I’ve came across something I don’t fully understand.

In a method like:

def method(self, blah):
    def __init__(?):
        ....
    ....

What does self do? What is it meant to be? Is it mandatory?

What does the __init__ method do? Why is it necessary? (etc.)

I think they might be OOP constructs, but I don’t know very much.


回答 0

在此代码中:

class A(object):
    def __init__(self):
        self.x = 'Hello'

    def method_a(self, foo):
        print self.x + ' ' + foo

self变量表示对象本身的实例。大多数面向对象的语言将此作为隐藏参数传递给在对象上定义的方法。Python没有。您必须明确声明它。创建A类的实例并调用其方法时,它将自动传递,如…

a = A()               # We do not pass any argument to the __init__ method
a.method_a('Sailor!') # We only pass a single argument

__init__方法大致代表了Python中的构造函数。调用时,A()Python为您创建一个对象,并将其作为第一个参数传递给__init__方法。任何其他参数(例如A(24, 'Hello'))也将作为参数传递-在这种情况下,会引发异常,因为构造函数不期望它们。

In this code:

class A(object):
    def __init__(self):
        self.x = 'Hello'

    def method_a(self, foo):
        print self.x + ' ' + foo

… the self variable represents the instance of the object itself. Most object-oriented languages pass this as a hidden parameter to the methods defined on an object; Python does not. You have to declare it explicitly. When you create an instance of the A class and call its methods, it will be passed automatically, as in …

a = A()               # We do not pass any argument to the __init__ method
a.method_a('Sailor!') # We only pass a single argument

The __init__ method is roughly what represents a constructor in Python. When you call A() Python creates an object for you, and passes it as the first parameter to the __init__ method. Any additional parameters (e.g., A(24, 'Hello')) will also get passed as arguments–in this case causing an exception to be raised, since the constructor isn’t expecting them.


回答 1

是的,您是对的,这些是oop结构。

__init__是一个类的构造函数。该self参数是指对象的实例(如this在C ++中)。

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

__init__分配对象的内存时将调用该方法:

x = Point(1,2)

self如果要将值与对象保持在一起,则在对象的方法内使用参数很重要。例如,如果您实现如下__init__方法:

class Point:
    def __init__(self, x, y):
        _x = x
        _y = y

您的xy参数将存储在堆栈中的变量中,并且当init方法超出范围时将被丢弃。将这些变量设置为self._x并将self._y这些变量设置为Point对象的成员(在对象的生存期内可访问)。

Yep, you are right, these are oop constructs.

__init__ is the constructor for a class. The self parameter refers to the instance of the object (like this in C++).

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

The __init__ method gets called when memory for the object is allocated:

x = Point(1,2)

It is important to use the self parameter inside an object’s method if you want to persist the value with the object. If, for instance, you implement the __init__ method like this:

class Point:
    def __init__(self, x, y):
        _x = x
        _y = y

Your x and y parameters would be stored in variables on the stack and would be discarded when the init method goes out of scope. Setting those variables as self._x and self._y sets those variables as members of the Point object (accessible for the lifetime of the object).


回答 2

简短的说明性示例

希望对您有所帮助,这是我用来理解在类内部声明的变量与在__init__函数内部声明的变量之间的区别的一个简单示例:

class MyClass(object):
    i = 123
    def __init__(self):
        self.i = 345

a = MyClass()
print(a.i)
print(MyClass.i)

输出:

345
123

A brief illustrative example

In the hope it might help a little, here’s a simple example I used to understand the difference between a variable declared inside a class, and a variable declared inside an __init__ function:

class MyClass(object):
    i = 123
    def __init__(self):
        self.i = 345

a = MyClass()
print(a.i)
print(MyClass.i)

Output:

345
123

回答 3

简而言之:

  1. self正如它暗示的那样,它指向自身 -调用该方法的对象。也就是说,如果您有N个对象调用该方法,则将self.a为N个对象中的每一个引用变量的单独实例。想象a每个对象的N个变量副本
  2. __init__在其他OOP语言(例如C ++ / Java)中称为构造函数。基本思想是它是一种特殊的方法,当创建该Class的对象时会自动调用该方法

In short:

  1. self as it suggests, refers to itself– the object which has called the method. That is, if you have N objects calling the method, then self.a will refer to a separate instance of the variable for each of the N objects. Imagine N copies of the variable a for each object
  2. __init__ is what is called as a constructor in other OOP languages such as C++/Java. The basic idea is that it is a special method which is automatically called when an object of that Class is created

回答 4

__init__确实像构造函数。如果希望它们充当非静态方法,则需要将“ self”作为第一个参数传递给任何类函数。“ self”是您的类的实例变量。

__init__ does act like a constructor. You’ll need to pass “self” to any class functions as the first argument if you want them to behave as non-static methods. “self” are instance variables for your class.


回答 5

试用此代码。希望它能帮助像我这样的许多C程序员学习Py。

#! /usr/bin/python2

class Person:

    '''Doc - Inside Class '''

    def __init__(self, name):
        '''Doc - __init__ Constructor'''
        self.n_name = name        

    def show(self, n1, n2):
        '''Doc - Inside Show'''
        print self.n_name
        print 'Sum = ', (n1 + n2)

    def __del__(self):
        print 'Destructor Deleting object - ', self.n_name

p=Person('Jay')
p.show(2, 3)
print p.__doc__
print p.__init__.__doc__
print p.show.__doc__

输出:

Jay

Sum = 5

Doc - Inside Class

Doc - __init__ Constructor

Doc - Inside Show

Destructor Deleting object - Jay

Try out this code. Hope it helps many C programmers like me to Learn Py.

#! /usr/bin/python2

class Person:

    '''Doc - Inside Class '''

    def __init__(self, name):
        '''Doc - __init__ Constructor'''
        self.n_name = name        

    def show(self, n1, n2):
        '''Doc - Inside Show'''
        print self.n_name
        print 'Sum = ', (n1 + n2)

    def __del__(self):
        print 'Destructor Deleting object - ', self.n_name

p=Person('Jay')
p.show(2, 3)
print p.__doc__
print p.__init__.__doc__
print p.show.__doc__

Output:

Jay

Sum = 5

Doc - Inside Class

Doc - __init__ Constructor

Doc - Inside Show

Destructor Deleting object - Jay


回答 6

类对象支持两种操作:属性引用和实例化

属性引用使用Python中所有属性引用使用的标准语法:obj.name。有效属性名称是创建类对象时在类命名空间中的所有名称。因此,如果类定义如下所示:

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

then MyClass.iMyClass.f是有效的属性引用,分别返回整数和函数对象。类属性也可以分配给它,因此您可以MyClass.i通过赋值来更改其值。__doc__也是有效的属性,返回属于该类的文档字符串:“简单示例类”。

类实例化使用函数表示法。只是假装类对象是一个无参数函数,它将返回该类的新实例。例如:

x = MyClass()

所述实例化操作(“呼叫”一类对象)来创建一个空对象。许多类喜欢创建具有定制为特定初始状态的实例的对象。因此,一个类可以定义一个名为的特殊方法__init__(),如下所示:

def __init__(self):
    self.data = []

当类定义__init__()方法时,类实例化将自动__init__()为新创建的类实例调用。因此,在此示例中,可以通过以下方式获取新的初始化实例:

x = MyClass()

当然,该__init__()方法可能具有更大的灵活性。在这种情况下,提供给类实例化运算符的参数将传递给__init__()。例如,

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i

摘自最终对我最大的帮助的官方文档


这是我的例子

class Bill():
    def __init__(self,apples,figs,dates):
        self.apples = apples
        self.figs = figs
        self.dates = dates
        self.bill = apples + figs + dates
        print ("Buy",self.apples,"apples", self.figs,"figs 
                and",self.dates,"dates. 
                Total fruitty bill is",self.bill," pieces of fruit :)")

创建Bill类的实例时:

purchase = Bill(5,6,7)

你得到:

> Buy 5 apples 6 figs and 7 dates. Total fruitty bill is 18  pieces of
> fruit :)

Class objects support two kinds of operations: attribute references and instantiation

Attribute references use the standard syntax used for all attribute references in Python: obj.name. Valid attribute names are all the names that were in the class’s namespace when the class object was created. So, if the class definition looked like this:

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively. Class attributes can also be assigned to, so you can change the value of MyClass.i by assignment. __doc__ is also a valid attribute, returning the docstring belonging to the class: “A simple example class”.

Class instantiation uses function notation. Just pretend that the class object is a parameterless function that returns a new instance of the class. For example:

x = MyClass()

The instantiation operation (“calling” a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named __init__(), like this:

def __init__(self):
    self.data = []

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the newly-created class instance. So in this example, a new, initialized instance can be obtained by:

x = MyClass()

Of course, the __init__() method may have arguments for greater flexibility. In that case, arguments given to the class instantiation operator are passed on to __init__(). For example,

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i

Taken from official documentation which helped me the most in the end.


Here is my example

class Bill():
    def __init__(self,apples,figs,dates):
        self.apples = apples
        self.figs = figs
        self.dates = dates
        self.bill = apples + figs + dates
        print ("Buy",self.apples,"apples", self.figs,"figs 
                and",self.dates,"dates. 
                Total fruitty bill is",self.bill," pieces of fruit :)")

When you create instance of class Bill:

purchase = Bill(5,6,7)

You get:

> Buy 5 apples 6 figs and 7 dates. Total fruitty bill is 18  pieces of
> fruit :)

回答 7

自己弄不清楚这个问题。即使在这里阅读答案。

要正确理解__init__方法,您需要了解自己。

自我参数

__init__方法接受的参数为:

def __init__(self, arg1, arg2):

但是我们实际上只传递了两个参数:

instance = OurClass('arg1', 'arg2')

多余的参数从何而来?

当我们访问对象的属性时,我们按名称(或按引用)进行操作。这里的实例是对我们新对象的引用。我们使用instance.printargs访问实例对象的printargs方法。

为了从__init__方法内部访问对象属性,我们需要对对象的引用。

每当调用方法时,对主对象的引用都会作为第一个参数传递。按照惯例,您总是将第一个参数称为方法自身。

这意味着__init__我们可以执行以下方法:

self.arg1 = arg1
self.arg2 = arg2

在这里,我们在对象上设置属性。您可以通过执行以下操作来验证这一点:

instance = OurClass('arg1', 'arg2')
print instance.arg1
arg1

这样的值称为对象属性。在这里,该__init__方法设置实例的arg1和arg2属性。

来源:http : //www.voidspace.org.uk/python/articles/OOP.shtml#the-init-method

Had trouble undestanding this myself. Even after reading the answers here.

To properly understand the __init__ method you need to understand self.

The self Parameter

The arguments accepted by the __init__ method are :

def __init__(self, arg1, arg2):

But we only actually pass it two arguments :

instance = OurClass('arg1', 'arg2')

Where has the extra argument come from ?

When we access attributes of an object we do it by name (or by reference). Here instance is a reference to our new object. We access the printargs method of the instance object using instance.printargs.

In order to access object attributes from within the __init__ method we need a reference to the object.

Whenever a method is called, a reference to the main object is passed as the first argument. By convention you always call this first argument to your methods self.

This means in the __init__ method we can do :

self.arg1 = arg1
self.arg2 = arg2

Here we are setting attributes on the object. You can verify this by doing the following :

instance = OurClass('arg1', 'arg2')
print instance.arg1
arg1

values like this are known as object attributes. Here the __init__ method sets the arg1 and arg2 attributes of the instance.

source: http://www.voidspace.org.uk/python/articles/OOP.shtml#the-init-method


回答 8

请注意,self实际上它可以是任何有效的python标识符。例如,我们可以从Chris B的示例中轻松编写:

class A(object):
    def __init__(foo):
        foo.x = 'Hello'

    def method_a(bar, foo):
        print bar.x + ' ' + foo

它的工作原理完全一样。但是,建议使用self,因为其他pythoner会更容易识别它。

note that self could actually be any valid python identifier. For example, we could just as easily write, from Chris B’s example:

class A(object):
    def __init__(foo):
        foo.x = 'Hello'

    def method_a(bar, foo):
        print bar.x + ' ' + foo

and it would work exactly the same. It is however recommended to use self because other pythoners will recognize it more easily.


回答 9

基本上,在同一类的多个函数中使用变量时,需要使用’self’关键字。至于init,它用于设置默认值,以防该类中没有其他函数被调用。

Basically, you need to use the ‘self’ keyword when using a variable in multiple functions within the same class. As for init, it’s used to setup default values incase no other functions from within that class are called.


回答 10

  1. __init__本质上是一个函数,一旦创建并与相应的类匹配,该函数将“初始化” / “激活”特定对象的类的属性。
  2. self 表示将继承这些属性的对象。
  1. __init__ is basically a function which will “initialize”/“activate” the properties of the class for a specific object, once created and matched to the corresponding class..
  2. self represents that object which will inherit those properties.

回答 11

“自我”是对类实例的引用

class foo:
    def bar(self):
            print "hi"

现在我们可以创建foo的实例并对其调用方法,在这种情况下,Python将添加self参数:

f = foo()
f.bar()

但是,如果方法调用不在类实例的上下文中,则也可以传递它,下面的代码执行相同的操作

f = foo()
foo.bar(f)

有趣的是,变量名“ self”只是一个约定。下面的定义将完全相同。.尽管说过,这是非常严格的约定,应该始终遵循,但是它确实说明了语言的灵活性。

class foo:
    def bar(s):
            print "hi"

The ‘self’ is a reference to the class instance

class foo:
    def bar(self):
            print "hi"

Now we can create an instance of foo and call the method on it, the self parameter is added by Python in this case:

f = foo()
f.bar()

But it can be passed in as well if the method call isn’t in the context of an instance of the class, the code below does the same thing

f = foo()
foo.bar(f)

Interestingly the variable name ‘self’ is just a convention. The below definition will work exactly the same.. Having said that it is very strong convention which should be followed always, but it does say something about flexible nature of the language

class foo:
    def bar(s):
            print "hi"

回答 12

只是演示的问题。

class MyClass:

    def __init__(self):
        print('__init__ is the constructor for a class')

    def __del__(self):
        print('__del__ is the destructor for a class')

    def __enter__(self):
        print('__enter__ is for context manager')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__ is for context manager')

    def greeting(self):
        print('hello python')


if __name__ == '__main__':
    with MyClass() as mycls:
        mycls.greeting()

$ python3 class.objects_instantiation.py
__init__ is the constructor for a class
__enter__ is for context manager
hello python
__exit__ is for context manager
__del__ is the destructor for a class

Just a demo for the question.

class MyClass:

    def __init__(self):
        print('__init__ is the constructor for a class')

    def __del__(self):
        print('__del__ is the destructor for a class')

    def __enter__(self):
        print('__enter__ is for context manager')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print('__exit__ is for context manager')

    def greeting(self):
        print('hello python')


if __name__ == '__main__':
    with MyClass() as mycls:
        mycls.greeting()

$ python3 class.objects_instantiation.py
__init__ is the constructor for a class
__enter__ is for context manager
hello python
__exit__ is for context manager
__del__ is the destructor for a class

回答 13

在此代码中:

class Cat:
    def __init__(self, name):
        self.name = name
    def info(self):
        print 'I am a cat and I am called', self.name

在此__init__充当类的构造函数,并在实例化对象时调用此函数。self表示实例化对象。

c = Cat('Kitty')
c.info()

以上语句的结果如下:

I am a cat and I am called Kitty

In this code:

class Cat:
    def __init__(self, name):
        self.name = name
    def info(self):
        print 'I am a cat and I am called', self.name

Here __init__ acts as a constructor for the class and when an object is instantiated, this function is called. self represents the instantiating object.

c = Cat('Kitty')
c.info()

The result of the above statements will be as follows:

I am a cat and I am called Kitty

回答 14

什么是自我呢?这是什么意思?是强制性的吗?

每个类方法(包括init)的第一个参数始终是对该类当前实例的引用。按照惯例,该参数始终命名为self。在init方法中,self指的是新创建的对象;在其他类方法中,它引用其方法被调用的实例。

Python 不会强迫您使用self ”。您可以为其指定任何名称。但是请记住,方法定义中的第一个参数是对对象的引用。Python self为您将参数添加到列表中。调用方法时不需要包含它。如果您没有提供self in init方法,则会收到错误消息

TypeError: __init___() takes no arguments (1 given)

什么是初始化方法吗?为什么有必要?(等等。)

init是初始化的缩写。它是一个构造函数,当您创建类的实例时会调用该构造函数,而不必这样做。但是通常我们的做法是编写用于设置对象默认状态的init方法。如果您最初不愿设置对象的任何状态,则无需编写此方法。

What does self do? What is it meant to be? Is it mandatory?

The first argument of every class method, including init, is always a reference to the current instance of the class. By convention, this argument is always named self. In the init method, self refers to the newly created object; in other class methods, it refers to the instance whose method was called.

Python doesn’t force you on usingself“. You can give it any name you want. But remember the first argument in a method definition is a reference to the object. Python adds the self argument to the list for you; you do not need to include it when you call the methods. if you didn’t provide self in init method then you will get an error

TypeError: __init___() takes no arguments (1 given)

What does the init method do? Why is it necessary? (etc.)

init is short for initialization. It is a constructor which gets called when you make an instance of the class and it is not necessary. But usually it our practice to write init method for setting default state of the object. If you are not willing to set any state of the object initially then you don’t need to write this method.


回答 15

# Source: Class and Instance Variables
# https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables

class MyClass(object):
    # class variable
    my_CLS_var = 10

    # sets "init'ial" state to objects/instances, use self argument
    def __init__(self):
        # self usage => instance variable (per object)
        self.my_OBJ_var = 15

        # also possible, class name is used => init class variable
        MyClass.my_CLS_var = 20


def run_example_func():
    # PRINTS    10    (class variable)
    print MyClass.my_CLS_var

    # executes __init__ for obj1 instance
    # NOTE: __init__ changes class variable above
    obj1 = MyClass()

    # PRINTS    15    (instance variable)
    print obj1.my_OBJ_var

    # PRINTS    20    (class variable, changed value)
    print MyClass.my_CLS_var


run_example_func()
# Source: Class and Instance Variables
# https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables

class MyClass(object):
    # class variable
    my_CLS_var = 10

    # sets "init'ial" state to objects/instances, use self argument
    def __init__(self):
        # self usage => instance variable (per object)
        self.my_OBJ_var = 15

        # also possible, class name is used => init class variable
        MyClass.my_CLS_var = 20


def run_example_func():
    # PRINTS    10    (class variable)
    print MyClass.my_CLS_var

    # executes __init__ for obj1 instance
    # NOTE: __init__ changes class variable above
    obj1 = MyClass()

    # PRINTS    15    (instance variable)
    print obj1.my_OBJ_var

    # PRINTS    20    (class variable, changed value)
    print MyClass.my_CLS_var


run_example_func()

回答 16

Python __init__及其作用self是什么?

怎么self办?这是什么意思?它是强制性的吗?

__init__方法做什么?为什么有必要?(等等。)

给出的示例不正确,因此让我基于它创建一个正确的示例:

class SomeObject(object):

    def __init__(self, blah):
        self.blah = blah

    def method(self):
        return self.blah 

当我们创建对象的实例时,将__init__在对象创建后调用,以自定义对象。也就是说,当我们SomeObject'blah'下面的方法(可能是任何东西)进行调用时,它将__init__作为参数传递给函数blah

an_object = SomeObject('blah')

self参数是实例SomeObject,将被分配到an_object

稍后,我们可能要在此对象上调用方法:

an_object.method()

进行点分查找,即将an_object.method实例绑定到该函数的实例,并且该方法(如上所述)现在是“绑定”方法-这意味着我们无需将实例显式传递给方法调用。

方法调用获取实例是因为它绑定在点分查找上,并在调用时执行其编程要执行的任何代码。

隐式传递的self参数self由约定调用。我们可以使用其他任何合法的Python名称,但是如果将其更改为其他名称,您可能会被其他Python程序员感到羞耻和ed愧。

__init__是一种特殊的方法,在Python数据模型文档中进行了介绍。在创建实例后立即调用它(通常通过__new__-,尽管__new__不是必需的,除非您将不可变的数据类型作为子类)。

Python __init__ and self what do they do?

What does self do? What is it meant to be? Is it mandatory?

What does the __init__ method do? Why is it necessary? (etc.)

The example given is not correct, so let me create a correct example based on it:

class SomeObject(object):

    def __init__(self, blah):
        self.blah = blah

    def method(self):
        return self.blah 

When we create an instance of the object, the __init__ is called to customize the object after it has been created. That is, when we call SomeObject with 'blah' below (which could be anything), it gets passed to the __init__ function as the argument, blah:

an_object = SomeObject('blah')

The self argument is the instance of SomeObject that will be assigned to an_object.

Later, we might want to call a method on this object:

an_object.method()

Doing the dotted lookup, that is, an_object.method, binds the instance to an instance of the function, and the method (as called above) is now a “bound” method – which means we do not need to explicitly pass the instance to the method call.

The method call gets the instance because it was bound on the dotted lookup, and when called, then executes whatever code it was programmed to perform.

The implicitly passed self argument is called self by convention. We could use any other legal Python name, but you will likely get tarred and feathered by other Python programmers if you change it to something else.

__init__ is a special method, documented in the Python datamodel documentation. It is called immediately after the instance is created (usually via __new__ – although __new__ is not required unless you are subclassing an immutable datatype).


回答 17

在这里,这家伙写得非常好而且很简单:https : //www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/

阅读以上链接作为对此的参考:

self?那么,所有客户方法的自我参数又如何呢?它是什么?当然,这就是实例!换句话说,诸如提款之类的方法定义了从某个抽象客户帐户中提款的指令。调用jeff.withdraw(100.0)会将这些指令用于jeff实例。

因此,当我们说def withdraw(self,amount):时,我们的意思是,“这是您如何从Customer对象(我们称为self)和一个美元图形(我们称为amount)提取钱的方法。是被调用提现的Customer的实例,也不是我做类比。jeff.withdraw(100.0)只是Customer.withdraw(jeff,100.0)的简写,这是完全有效的(如果不经常出现)码。

init自我可能对其他方法有意义,但是init呢?当我们调用init时,我们正在创建一个对象,那么如何已经存在一个self?Python允许我们将self模式扩展到构造对象的时间,即使它并不完全适合。试想一下jeff = Customer(’Jeff Knupp’,1000.0)与调用jeff = Customer(jeff,’Jeff Knupp’,1000.0)相同;传入的jeff也成为结果。

这就是为什么当我们调用init时,我们通过说出self.name = name之类的东西来初始化对象。请记住,由于self是实例,所以这等效于说jeff.name = name,与jeff.name =’Jeff Knupp相同。同样,self.balance = balance与jeff.balance = 1000.0相同。在这两行之后,我们认为“客户”对象已“初始化”并可以使用。

小心你 __init__

经过初始化完成后,主叫方可以正确地假定对象就可以使用了。也就是说,在jeff = Customer(’Jeff Knupp’,1000.0)之后,我们可以开始对jeff进行入金和出金;jeff是一个完全初始化的对象。

Here, the guy has written pretty well and simple: https://www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/

Read above link as a reference to this:

self? So what’s with that self parameter to all of the Customer methods? What is it? Why, it’s the instance, of course! Put another way, a method like withdraw defines the instructions for withdrawing money from some abstract customer’s account. Calling jeff.withdraw(100.0) puts those instructions to use on the jeff instance.

So when we say def withdraw(self, amount):, we’re saying, “here’s how you withdraw money from a Customer object (which we’ll call self) and a dollar figure (which we’ll call amount). self is the instance of the Customer that withdraw is being called on. That’s not me making analogies, either. jeff.withdraw(100.0) is just shorthand for Customer.withdraw(jeff, 100.0), which is perfectly valid (if not often seen) code.

init self may make sense for other methods, but what about init? When we call init, we’re in the process of creating an object, so how can there already be a self? Python allows us to extend the self pattern to when objects are constructed as well, even though it doesn’t exactly fit. Just imagine that jeff = Customer(‘Jeff Knupp’, 1000.0) is the same as calling jeff = Customer(jeff, ‘Jeff Knupp’, 1000.0); the jeff that’s passed in is also made the result.

This is why when we call init, we initialize objects by saying things like self.name = name. Remember, since self is the instance, this is equivalent to saying jeff.name = name, which is the same as jeff.name = ‘Jeff Knupp. Similarly, self.balance = balance is the same as jeff.balance = 1000.0. After these two lines, we consider the Customer object “initialized” and ready for use.

Be careful what you __init__

After init has finished, the caller can rightly assume that the object is ready to use. That is, after jeff = Customer(‘Jeff Knupp’, 1000.0), we can start making deposit and withdraw calls on jeff; jeff is a fully-initialized object.