Suche…


Einführung

Python bietet sich nicht nur als beliebte Skriptsprache an, sondern unterstützt auch das objektorientierte Programmierparadigma. Klassen beschreiben Daten und stellen Methoden zum Manipulieren dieser Daten bereit, die alle in einem einzigen Objekt enthalten sind. Darüber hinaus ermöglichen Klassen eine Abstraktion, indem konkrete Implementierungsdetails von abstrakten Repräsentationen von Daten getrennt werden.

Code, der Klassen verwendet, ist im Allgemeinen einfacher zu lesen, zu verstehen und zu verwalten.

Grundvererbung

Die Vererbung in Python basiert auf ähnlichen Ideen, die in anderen objektorientierten Sprachen wie Java, C ++ usw. verwendet werden. Eine neue Klasse kann wie folgt von einer vorhandenen Klasse abgeleitet werden.

class BaseClass(object):
    pass

class DerivedClass(BaseClass):
    pass

Die BaseClass ist die bereits bestehende (Eltern) Klasse und die DerivedClass ist die neue (Kind) Klasse , die (oder Unterklassen) Attribute erbt BaseClass . Hinweis : Ab Python 2.2 erben alle Klassen implizit von der object , der Basisklasse für alle integrierten Typen.

Im folgenden Beispiel definieren wir eine übergeordnete Rectangle , die implizit vom object erbt:

class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h
    
    def area(self):
        return self.w * self.h
        
    def perimeter(self):
        return 2 * (self.w + self.h)

Die Rectangle kann als Basisklasse zum Definieren einer Square , da ein Quadrat ein Sonderfall eines Rechtecks ​​ist.

class Square(Rectangle):
    def __init__(self, s):
        # call parent constructor, w and h are both s
        super(Square, self).__init__(s, s)
        self.s = s

Die Square Klasse erbt automatisch alle Attribute der Rectangle Klasse sowie der Objektklasse. super() wird verwendet, um die __init__() Methode der Rectangle Klasse aufzurufen, wobei im Wesentlichen alle überschriebenen Methoden der Basisklasse __init__() . Hinweis : Für super() in Python 3 keine Argumente erforderlich.

Abgeleitete Klassenobjekte können auf die Attribute ihrer Basisklassen zugreifen und diese ändern:

r.area()
# Output: 12
r.perimeter()  
# Output: 14

s.area()
# Output: 4
s.perimeter()
# Output: 8

Integrierte Funktionen, die mit Vererbung arbeiten

issubclass(DerivedClass, BaseClass) : issubclass(DerivedClass, BaseClass) True wenn DerivedClass eine Unterklasse der BaseClass

isinstance(s, Class) : isinstance(s, Class) True wenn s eine Instanz von Class oder eine der abgeleiteten Klassen von Class

# subclass check        
issubclass(Square, Rectangle)
# Output: True

# instantiate
r = Rectangle(3, 4)
s = Square(2)

isinstance(r, Rectangle)  
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square

isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True

Klassen- und Instanzvariablen

Instanzvariablen sind für jede Instanz eindeutig, während Klassenvariablen von allen Instanzen gemeinsam genutzt werden.

class C:
    x = 2  # class variable

    def __init__(self, y):
        self.y = y  # instance variable

C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'

c1 = C(3)
c1.x
# 2
c1.y
# 3

c2 = C(4)
c2.x
# 2
c2.y
# 4

Auf Klassenvariablen kann auf Instanzen dieser Klasse zugegriffen werden. Wenn Sie jedoch das Klassenattribut zuweisen, wird eine Instanzvariable erstellt, die die Klassenvariable überschattet

c2.x = 4
c2.x
# 4
C.x
# 2

Beachten Sie, dass das Ändern von Klassenvariablen aus Instanzen zu unerwarteten Konsequenzen führen kann.

class D:
    x = []
    def __init__(self, item):
        self.x.append(item)  # note that this is not an assigment!

d1 = D(1)
d2 = D(2)

d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]

Gebundene, ungebundene und statische Methoden

Die Idee von gebundenen und ungebundenen Methoden wurde in Python 3 entfernt . Wenn Sie in Python 3 eine Methode innerhalb einer Klasse deklarieren, verwenden Sie ein def Schlüsselwort und erstellen so ein Funktionsobjekt. Dies ist eine reguläre Funktion, und die umgebende Klasse fungiert als Namespace. Im folgenden Beispiel deklarieren wir die Methode f innerhalb der Klasse A und werden zu einer Funktion Af :

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

In Python 2 war das Verhalten anders: Funktionsobjekte innerhalb der Klasse wurden implizit durch Objekte des Typs instancemethod , die als ungebundene Methoden bezeichnet wurden, da sie nicht an eine bestimmte Klasseninstanz gebunden waren. Es war möglich, auf die zugrunde liegende Funktion mit der .__func__ .

Python 2.x 2.3
A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

Letztere Verhaltensweisen werden durch Inspektion bestätigt - Methoden werden in Python 3 als Funktionen erkannt, während die Unterscheidung in Python 2 beibehalten wird.

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

In beiden Versionen von Python kann die Funktion / Methode Af direkt aufgerufen werden, vorausgesetzt, Sie übergeben eine Instanz der Klasse A als erstes Argument.

A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
#                      A instance as first argument (got int instance instead) 
# Python 3: 14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

Angenommen, a ist eine Instanz der Klasse A , was ist dann af ? Nun, intuitiv sollte dies die gleiche Methode f der Klasse A , es sollte nur irgendwie "wissen", dass sie auf das Objekt a angewendet wurde - in Python wird diese Methode als an a gebunden .

Die wichtigsten Details sind folgende: Das Schreiben von af ruft die magische __getattribute__ Methode von a , die zuerst prüft, ob a ein Attribut mit dem Namen f (nicht), und dann die Klasse A überprüft, ob sie eine Methode mit einem solchen Namen enthält (tut dies) und erstellt ein neues Objekt m vom Typ method das den Verweis auf das ursprüngliche Af in m.__func__ und einen Verweis auf das Objekt a in m.__self__ . Wenn dieses Objekt als Funktion aufgerufen wird, führt es einfach Folgendes aus: m(...) => m.__func__(m.__self__, ...) . Daher wird dieses Objekt als gebundene Methode bezeichnet, da es beim Aufruf weiß, das Objekt anzugeben, an das es gebunden war, als erstes Argument. (Diese Dinge funktionieren in Python 2 und 3 auf dieselbe Weise).

a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4

# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f  # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f  # True

Schließlich verfügt Python über Klassenmethoden und statische Methoden - spezielle Arten von Methoden. Klassenmethoden funktionieren auf die gleiche Weise wie reguläre Methoden, außer dass sie beim Aufruf an ein Objekt an die Klasse des Objekts und nicht an das Objekt gebunden werden. Also ist m.__self__ = type(a) . Wenn Sie eine solche gebundene Methode aufrufen, übergibt sie die Klasse a als erstes Argument. Statische Methoden sind noch einfacher: Sie binden gar nichts und geben die zugrunde liegende Funktion einfach ohne Transformationen zurück.

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        return cls.multiplier * x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world

Beachten Sie, dass Klassenmethoden auch dann an die Klasse gebunden sind, wenn auf die Instanz zugegriffen wird:

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20

Es ist erwähnenswert, dass auf der untersten Ebene Funktionen, Methoden, statische Methoden usw. eigentlich Deskriptoren sind , die die speziellen Methoden __get__ , __set __ und optional __del__ . Weitere Informationen zu Klassenmethoden und statischen Methoden finden Sie unter:

Klassen im neuen Stil und im alten Stil

Python 2.x 2.2.0

In Python 2.2 wurden Klassen mit neuem Stil eingeführt, um Klassen und Typen zu vereinheitlichen. Sie erben vom object der obersten Ebene. Eine Klasse im neuen Stil ist ein benutzerdefinierter Typ und ist den integrierten Typen sehr ähnlich.

# new-style class
class New(object):
    pass

# new-style instance
new = New()

new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True

Klassen im alten Stil erben nicht vom object . Im alten Stil Instanzen sind immer mit einem eingebauten in implementierten instance Art.

# old-style class
class Old:
    pass

# old-style instance
old = Old()

old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
Python 3.x 3.0.0

In Python 3 wurden Klassen alten Stils entfernt.

Klassen im neuen Stil in Python 3 erben implizit vom object , so dass keine Angabe von MyClass(object) mehr erforderlich ist.

class MyClass:
    pass

my_inst = MyClass()

type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True

Standardwerte für Instanzvariablen

Wenn die Variable einen Wert eines unveränderlichen Typs enthält (z. B. eine Zeichenfolge), können Sie einen solchen Standardwert zuweisen

class Rectangle(object):
    def __init__(self, width, height, color='blue'):
        self.width = width
        self.height = height
        self.color = color
    
    def area(self):
        return self.width  * self.height 

# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue

red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red

Beim Initialisieren von veränderlichen Objekten wie Listen im Konstruktor muss vorsichtig vorgegangen werden. Betrachten Sie das folgende Beispiel:

class Rectangle2D(object):
    def __init__(self, width, height, pos=[0,0], color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2's pos has changed as well

Dieses Verhalten wird durch die Tatsache verursacht, dass in Python Standardparameter bei der Funktionsausführung und nicht bei der Funktionsdeklaration gebunden sind. Um eine Standardinstanzvariable zu erhalten, die nicht von Instanzen gemeinsam genutzt wird, sollte ein Konstrukt wie das folgende verwendet werden:

class Rectangle2D(object):
    def __init__(self, width, height, pos=None, color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos or [0, 0] # default value is [0, 0]
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2's pos hasn't changed 

Siehe auch Mutable Default Arguments und "Least Astonishment" und das Mutable Default Argument .

Mehrfachvererbung

Python verwendet den C3-Linearisierungsalgorithmus , um die Reihenfolge zu bestimmen, in der Klassenattribute aufgelöst werden, einschließlich Methoden. Dies wird als Method Resolution Order (MRO) bezeichnet.

Hier ist ein einfaches Beispiel:

class Foo(object):
    foo = 'attr foo of Foo'
    

class Bar(object):
    foo = 'attr foo of Bar' # we won't see this.
    bar = 'attr bar of Bar'

class FooBar(Foo, Bar):
    foobar = 'attr foobar of FooBar'

Wenn wir nun FooBar instanziieren, wenn wir das foo-Attribut nachschlagen, sehen wir, dass das Attribut von Foo zuerst gefunden wird

fb = FooBar()

und

>>> fb.foo
'attr foo of Foo'

Hier ist der MRO von FooBar:

>>> FooBar.mro()
[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]

Es kann einfach gesagt werden, dass der Python-MRO-Algorithmus ist

  1. Tiefe zuerst (zB FooBar dann Foo ), sofern nicht
  2. Ein gemeinsam genutztes übergeordnetes object ( object ) wird von einem untergeordneten Bar ( Bar ) und blockiert
  3. Keine zirkulären Beziehungen erlaubt.

Das heißt, Bar kann nicht von FooBar erben, während FooBar von Bar erbt.

Ein umfassendes Beispiel in Python finden Sie im Wikipedia-Eintrag .

Ein weiteres mächtiges Feature in der Vererbung ist super . super kann übergeordnete Klassenfunktionen abrufen.

class Foo(object):
    def foo_method(self):
        print "foo Method"

class Bar(object):
    def bar_method(self):
        print "bar Method"

class FooBar(Foo, Bar):
    def foo_method(self):
        super(FooBar, self).foo_method()

Mehrfachvererbung mit der init-Methode der Klasse. Wenn jede Klasse eine eigene init-Methode hat, versuchen wir die Mehrfachvererbung. Dann wird nur die init-Methode der Klasse aufgerufen, die zuerst erbt.

für folgende Beispiel nur init - Methode Foo Klasse immer genannt Bar Klasse init nicht immer genannt

    class Foo(object):
        def __init__(self):
            print "foo init"

    class Bar(object):
        def __init__(self):
            print "bar init"

    class FooBar(Foo, Bar):
        def __init__(self):
            print "foobar init"
            super(FooBar, self).__init__()

    a = FooBar()

Ausgabe:

    foobar init
    foo init

Das bedeutet jedoch nicht, dass die Bar- Klasse nicht erbt. Die Instanz der finalen FooBar- Klasse ist auch eine Instanz der Bar- Klasse und der Foo- Klasse.

print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar) 

Ausgabe:

True
True
True

Deskriptoren und gepunktete Lookups

Deskriptoren sind Objekte, die (in der Regel) Attribute von Klassen sind und über besondere Methoden von __get__ , __set__ oder __delete__ .

Datenbezeichner jeder haben __set__ oder __delete__

Diese können die gepunktete Suche in einer Instanz steuern und werden zum Implementieren von Funktionen, staticmethod , classmethod und property . Eine gepunktete Lookup (zB Instanz foo der Klasse Foo aufzublicken Attribut bar - dh foo.bar ) verwendet den folgenden Algorithmus:

  1. bar wird in der Klasse Foo , Foo . Wenn es dort ist und ein Datendeskriptor ist , wird der Datendeskriptor verwendet. Auf diese Weise kann eine property Zugriff auf Daten in einer Instanz steuern, und Instanzen können dies nicht überschreiben. Wenn kein Data Descriptor vorhanden ist, dann

  2. bar wird in der Instanz __dict__ . Deshalb können wir Methoden, die von einer Instanz aufgerufen werden, mit einer gepunkteten Suche überschreiben oder blockieren. Wenn in der Instanz ein bar vorhanden ist, wird dieser verwendet. Wenn nicht, dann wir

  3. In der Klasse Foo nach bar suchen. Wenn es sich um einen Deskriptor handelt , wird das Deskriptorprotokoll verwendet. So werden Funktionen (in diesem Zusammenhang ungebundene Methoden), classmethod und staticmethod implementiert. Sonst gibt es einfach das Objekt dort zurück oder es gibt einen AttributeError

Klassenmethoden: alternative Initialisierer

Klassenmethoden bieten alternative Möglichkeiten zum Erstellen von Klasseninstanzen. Schauen wir uns ein Beispiel an.

Nehmen wir an, wir haben eine relativ einfache Person Klasse:

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

Es kann praktisch sein, Instanzen dieser Klasse zu erstellen, die einen vollständigen Namen anstelle des Vor- und Nachnamens angeben. Eine Möglichkeit, dies zu tun, besteht darin, last_name ein optionaler Parameter zu sein. last_name dieser Parameter nicht angegeben wird, haben wir den vollständigen Namen in folgender last_name :

class Person(object):

    def __init__(self, first_name, age, last_name=None):
        if last_name is None:
            self.first_name, self.last_name = first_name.split(" ", 2)
        else:
            self.first_name = first_name
            self.last_name = last_name
        
        self.full_name = self.first_name + " " + self.last_name
        self.age = age

    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

Es gibt jedoch zwei Hauptprobleme mit diesem Codebit:

  1. Die Parameter first_name und last_name sind jetzt irreführend, da Sie einen vollständigen Namen für first_name . Wenn es mehr Fälle und / oder mehr Parameter gibt, die diese Art von Flexibilität aufweisen, kann die Verzweigung von if / elif / else schnell ärgerlich werden.

  2. Nicht ganz so wichtig, aber dennoch hervorzuheben: Was ist, wenn last_name None , aber first_name nicht durch Leerzeichen in zwei oder mehr Dinge aufgeteilt wird? Wir haben noch eine weitere Ebene der Eingabevalidierung und / oder Ausnahmebehandlung ...

Klassenmethoden eingeben from_full_name eines einzelnen Initialisierers erstellen wir einen separaten Initialisierer mit dem Namen from_full_name und dekorieren ihn mit dem (integrierten) classmethod Dekorator.

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    @classmethod
    def from_full_name(cls, name, age):
        if " " not in name:
            raise ValueError
        first_name, last_name = name.split(" ", 2)
        return cls(first_name, last_name, age)
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

Beachten Sie cls statt self als erstes Argument für from_full_name . Klassenmethoden sind auf die Gesamtklasse angewendet wird , nicht eine Instanz einer bestimmten Klasse (was self bezeichnet in der Regel). Also, wenn cls unsere ist Person - Klasse, dann ist der zurückgegebene Wert von der from_full_name Klassenmethode ist Person(first_name, last_name, age) , die verwendete Person ‚s __init__ eine Instanz der erstellen Person Klasse. Wenn wir insbesondere eine Unterklasse Employee of Person from_full_name , funktioniert from_full_name auch in der Employee Klasse.

Um zu zeigen, dass dies wie erwartet funktioniert, erstellen wir Instanzen von Person auf mehrere __init__ , ohne die Verzweigung in __init__ :

In [2]: bob = Person("Bob", "Bobberson", 42)

In [3]: alice = Person.from_full_name("Alice Henderson", 31)

In [4]: bob.greet()
Hello, my name is Bob Bobberson.

In [5]: alice.greet()
Hello, my name is Alice Henderson.

Weitere Referenzen:

Klassenaufbau

Die Klassenzusammensetzung erlaubt explizite Beziehungen zwischen Objekten. In diesem Beispiel leben Menschen in Städten, die zu Ländern gehören. Die Zusammensetzung ermöglicht es den Menschen, auf die Anzahl aller in ihrem Land lebenden Menschen zuzugreifen:

class Country(object):
    def __init__(self):
        self.cities=[]
        
    def addCity(self,city):
        self.cities.append(city)
        

class City(object):
    def __init__(self, numPeople):
        self.people = []
        self.numPeople = numPeople
        
        
    def addPerson(self, person):
        self.people.append(person)
    
    def join_country(self,country):
        self.country = country
        country.addCity(self)
        
        for i in range(self.numPeople):
                person(i).join_city(self)
  

class Person(object):
    def __init__(self, ID):
        self.ID=ID

    def join_city(self, city):
        self.city = city
        city.addPerson(self)
        
    def people_in_my_country(self):
        x= sum([len(c.people) for c in self.city.country.cities])
        return x
        
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)

print(US.cities[0].people[0].people_in_my_country())

# 15

Affen-Patching

In diesem Fall bedeutet "Affen-Patching", dass einer Klasse eine neue Variable oder Methode hinzugefügt wird, nachdem diese definiert wurde. Angenommen, wir haben Klasse A als definiert

class A(object):
    def __init__(self, num):
        self.num = num

    def __add__(self, other):
        return A(self.num + other.num)

Aber jetzt möchten wir später im Code eine weitere Funktion hinzufügen. Angenommen, diese Funktion ist wie folgt.

def get_num(self):
    return self.num

Aber wie fügen wir das als Methode in A ? Das ist einfach, wir legen diese Funktion im Wesentlichen in A mit einer Zuweisungsanweisung ab.

A.get_num = get_num

Warum funktioniert das? Denn Funktionen sind Objekte wie jedes andere Objekt und Methoden sind Funktionen, die zur Klasse gehören.

Die Funktion get_num soll für alle vorhandenen (bereits erstellten) sowie für die neuen Instanzen von A verfügbar sein

Diese Zusätze sind automatisch für alle Instanzen dieser Klasse (oder ihrer Unterklassen) verfügbar. Zum Beispiel:

foo = A(42)

A.get_num = get_num

bar = A(6);

foo.get_num() # 42

bar.get_num() # 6

Beachten Sie, dass diese Technik im Gegensatz zu einigen anderen Sprachen nicht für bestimmte integrierte Typen geeignet ist und nicht als guter Stil betrachtet wird.

Alle Klassenmitglieder auflisten

Die Funktion dir() kann verwendet werden, um eine Liste der Mitglieder einer Klasse abzurufen:

dir(Class)

Zum Beispiel:

>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Es ist üblich, nur nach "nicht-magischen" Mitgliedern zu suchen. Dies kann mit einem einfachen Verständnis durchgeführt werden, in dem Mitglieder aufgelistet werden, deren Namen nicht mit __ :

>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Vorsichtsmaßnahmen:

Klassen können eine __dir__() -Methode definieren. Wenn diese Methode vorhanden ist, wird beim Aufruf von dir() __dir__() aufgerufen. __dir__() versucht Python, eine Liste der Mitglieder der Klasse zu erstellen. Dies bedeutet, dass die dir-Funktion unerwartete Ergebnisse haben kann. Zwei wichtige Zitate aus der offiziellen Python-Dokumentation :

Wenn das Objekt dir () nicht bereitstellt, versucht die Funktion, Informationen aus dem Dict- Attribut des Objekts (falls definiert) und aus seinem Typobjekt zu sammeln. Die Ergebnisliste ist nicht notwendigerweise vollständig und kann ungenau sein, wenn das Objekt über eine benutzerdefinierte getattr () verfügt.

Hinweis: Da dir () hauptsächlich zur Vereinfachung der Verwendung an einer interaktiven Eingabeaufforderung bereitgestellt wird, versucht es, einen interessanten Satz von Namen anzugeben, anstatt einen rigoros oder konsistent definierten Satz von Namen anzugeben, und sein detailliertes Verhalten kann sich ändern Veröffentlichungen. Beispielsweise sind Metaklassenattribute nicht in der Ergebnisliste enthalten, wenn das Argument eine Klasse ist.

Einführung in den Unterricht

Eine Klasse fungiert als Vorlage, die die grundlegenden Merkmale eines bestimmten Objekts definiert. Hier ist ein Beispiel:

class Person(object):
     """A simple class."""                            # docstring
     species = "Homo Sapiens"                         # class attribute

     def __init__(self, name):                        # special method
         """This is the initializer. It's a special
         method (see below).
         """
         self.name = name                             # instance attribute

     def __str__(self):                               # special method
         """This method is run when Python tries 
         to cast the object to a string. Return 
         this string when using print(), etc.
         """
         return self.name

     def rename(self, renamed):                       # regular method
         """Reassign and print the name attribute."""
         self.name = renamed
         print("Now my name is {}".format(self.name))

Es gibt ein paar Dinge zu beachten, wenn Sie das obige Beispiel betrachten.

  1. Die Klasse besteht aus Attributen (Daten) und Methoden (Funktionen).
  2. Attribute und Methoden werden einfach als normale Variablen und Funktionen definiert.
  3. Wie in dem entsprechenden Dokumentstring angegeben, wird die Methode __init__() als Initialisierer bezeichnet . Sie entspricht dem Konstruktor in anderen objektorientierten Sprachen und ist die Methode, die zuerst ausgeführt wird, wenn Sie ein neues Objekt oder eine neue Instanz der Klasse erstellen.
  4. Attribute, die für die gesamte Klasse gelten, werden zuerst definiert und als Klassenattribute bezeichnet .
  5. Attribute, die für eine bestimmte Instanz einer Klasse (ein Objekt) gelten, werden als Instanzattribute bezeichnet . Sie sind im Allgemeinen in __init__() . Dies ist nicht erforderlich, wird jedoch empfohlen (da außerhalb von __init__() definierte Attribute das Risiko __init__() dem Definieren darauf zugegriffen zu werden).
  6. Jede in der Klassendefinition enthaltene Methode übergibt das betreffende Objekt als ersten Parameter. Das Wort self wird für diesen Parameter verwendet (die Verwendung von self ist eigentlich eine Konvention, da das Wort self in Python keine inhärente Bedeutung hat, aber dies ist eine der am meisten respektierten Konventionen von Python, und Sie sollten es immer beachten).
  7. Diejenigen, die zur objektorientierten Programmierung in anderen Sprachen verwendet werden, können durch einige Dinge überrascht sein. Eines ist, dass Python kein reales Konzept private Elemente hat. Daher ahmt alles standardmäßig das Verhalten des public C ++ / Java-Schlüsselworts nach. Weitere Informationen finden Sie im Beispiel "Private Class Members" auf dieser Seite.
  8. Einige Methoden der Klasse haben folgende Form: __functionname__(self, other_stuff) . Alle diese Methoden werden "magische Methoden" genannt und sind ein wichtiger Bestandteil von Klassen in Python. Beispielsweise wird die Überladung von Operatoren in Python mit magischen Methoden implementiert. Weitere Informationen finden Sie in der entsprechenden Dokumentation .

Lassen Sie uns nun einige Beispiele unserer Person Klasse machen!

>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")

Wir haben derzeit drei Person , kelly , joseph und john_doe .

Mit dem Punktoperator können wir von jeder Instanz aus auf die Attribute der Klasse zugreifen . Beachten Sie noch einmal den Unterschied zwischen Klassen- und Instanzattributen:

>>> # Attributes
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'

Wir können die Methoden der Klasse mit demselben Punktoperator ausführen . :

>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>>  print(john_doe)
'John Doe'
>>>  john_doe.rename("John")
'Now my name is John'

Eigenschaften

Python-Klassen unterstützen Eigenschaften , die wie reguläre Objektvariablen aussehen, jedoch die Möglichkeit haben, benutzerdefiniertes Verhalten und Dokumentation anzufügen.

class MyClass(object):

    def __init__(self):
       self._my_string = ""
    
    @property
    def string(self):
        """A profoundly important string."""
        return self._my_string

    @string.setter
    def string(self, new_value):
        assert isinstance(new_value, str), \
               "Give me a string, not a %r!" % type(new_value)
        self._my_string = new_value

    @string.deleter
    def x(self):
        self._my_string = None

Die Objekte der Klasse MyClass haben anscheinend eine Eigenschaft .string . Das Verhalten wird jedoch jetzt streng kontrolliert:

mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string

Neben der nützlichen Syntax wie oben ermöglicht die Eigenschaftssyntax die Validierung oder das Hinzufügen anderer Erweiterungen zu diesen Attributen. Dies kann besonders bei öffentlichen APIs nützlich sein, bei denen dem Benutzer eine Hilfestellung gegeben werden sollte.

Eine weitere häufige Verwendung von Eigenschaften besteht darin, dass die Klasse "virtuelle Attribute" darstellen kann. Attribute, die nicht gespeichert werden, sondern nur auf Anforderung berechnet werden.

class Character(object):
    def __init__(name, max_hp):
        self._name = name
        self._hp = max_hp
        self._max_hp = max_hp

    # Make hp read only by not providing a set method
    @property
    def hp(self):
        return self._hp

    # Make name read only by not providing a set method
    @property
    def name(self):
        return self.name

    def take_damage(self, damage):
        self.hp -= damage
        self.hp = 0 if self.hp <0 else self.hp

    @property
    def is_alive(self):
        return self.hp != 0

    @property
    def is_wounded(self):
        return self.hp < self.max_hp if self.hp > 0 else False

    @property
    def is_dead(self):
        return not self.is_alive

bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200        
# out : AttributeError: can't set attribute
# hp attribute is read only.

bilbo.is_alive
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )

bilbo.hp
# out : 50

bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )
bilbo.hp
# out : 0

bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True

Singleton-Klasse

Ein Singleton ist ein Muster, das die Instantiierung einer Klasse auf eine Instanz / ein Objekt beschränkt. Weitere Informationen zu Python-Singleton-Entwurfsmustern finden Sie hier .

class Singleton:
    def __new__(cls):
        try:
            it = cls.__it__
        except AttributeError:
            it = cls.__it__ = object.__new__(cls)
        return it

    def __repr__(self):
        return '<{}>'.format(self.__class__.__name__.upper())

    def __eq__(self, other):
         return other is self

Eine andere Methode ist, Ihre Klasse zu schmücken. Anhand des Beispiels aus dieser Antwort erstellen Sie eine Singleton-Klasse:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

Zur Verwendung können Sie die Instance verwenden

@Singleton
class Single:
    def __init__(self):
        self.name=None
        self.val=0
    def getName(self):
        print(self.name)

x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow