web-dev-qa-db-ja.com

Python拡張機能-super()を使用Python 3 vs Python 2

もともと私は この質問 を聞きたかったのですが、それからそれはすでに考えられていたことがわかりました...

あちこち探して extending configparser のこの例を見つけました。以下はPython 3で動作します:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

しかし、Python 2ではそうではありません:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

その後、Python New Class vs. Old Classスタイルを少し読みます(例 here

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

しかし、initを呼び出すべきではありませんか?これはPython 2に相当しますか:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)
86
Oz123
  • super() (引数なし)はPython 3で導入されました(__class__とともに):

    super() -> same as super(__class__, self)
    

    したがって、新しいスタイルのクラスのPython 2と同等になります。

    super(CurrentClass, self)
    
  • 古いスタイルのクラスでは、常に使用できます:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    
135
mata

単一継承の場合(1つのクラスのみをサブクラス化する場合)、新しいクラスは基本クラスのメソッドを継承します。これには__init__が含まれます。したがって、クラスで定義しない場合は、ベースから取得します。

複数の継承(一度に複数のクラスをサブクラス化)を導入すると、事態は複雑になります。これは、複数の基本クラスに__init__がある場合、クラスは最初のクラスのみを継承するためです。

このような場合、可能であればsuperを使用する必要があります。その理由を説明します。しかし、常にできるとは限りません。問題は、すべての基本クラスもそれを使用しなければならないことです(そしてその基本クラスもツリー全体)。

その場合は、これも正常に機能します(Python 3で、Python 2に作り直すことができます-superもあります)。

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

独自の基本クラスがなくても、両方の基本クラスがsuperを使用する方法に注目してください。

superは、MROの次のクラスからメソッドを呼び出します(メソッド解決順序)。 CのMROは、(C, A, B, object)です。 C.__mro__を印刷して確認できます。

したがって、CAから__init__を継承し、A.__init__superB.__init__を呼び出します(BはMROのAに続きます)。

したがって、Cで何もしないと、両方を呼び出すことになります。

superを使用していない場合、(以前と同様に)A.__init__を継承することになりますが、今回はB.__init__を呼び出すものは何もありません。

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

これを修正するには、C.__init__を定義する必要があります:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

それに関する問題は、より複雑なMIツリーでは、一部のクラスの__init__メソッドが複数回呼び出されることがありますが、super/MROはそれらが一度だけ呼び出されることを保証することです。

44
yak

要するに、それらは同等です。履歴ビューを見てみましょう:

(1)最初は、関数は次のようになります。

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2)コードをより抽象的な(そしてより移植性の高い)にする。スーパークラスを取得する一般的な方法は、次のように考案されています。

    super(<class>, <instance>)

また、init関数には次のものがあります。

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

ただし、クラスとインスタンスの両方を明示的に渡す必要があると、DRY(自分自身を繰り返さないでください)規則に少し違反します。

(3)V3。よりスマートです

    super()

ほとんどの場合で十分です。 http://www.python.org/dev/peps/pep-3135/ を参照できます

22
wuliang

Python 3の簡単で完全な例がありますが、ほとんどの人が現在使用しているようです。

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

与える

42
chickenman
20
Jonathan Mugan

Super()での抽象クラスの使用を含む別のpython3実装。あなたはそれを覚えておくべきです

super().init(name、10)

と同じ効果があります

Person .init(self、name、10)

Super()には隠された「自己」があるので、同じオブジェクトがスーパークラスのinitメソッドに渡され、それを呼び出したオブジェクトに属性が追加されることに注意してください。したがって、super()getsはPersonに変換され、非表示のselfを含めると、上記のコードフラグメントが取得されます。

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())
1
SeasonalShot