Python Language
Klassen
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
:
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__
.
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.
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
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:
- Was ist der Unterschied zwischen @staticmethod und @classmethod in Python?
- Bedeutung von @classmethod und @staticmethod für Anfänger?
Klassen im neuen Stil und im alten Stil
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
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
- Tiefe zuerst (zB
FooBar
dannFoo
), sofern nicht - Ein gemeinsam genutztes übergeordnetes
object
(object
) wird von einem untergeordnetenBar
(Bar
) und blockiert - 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:
bar
wird in der KlasseFoo
,Foo
. Wenn es dort ist und ein Datendeskriptor ist , wird der Datendeskriptor verwendet. Auf diese Weise kann eineproperty
Zugriff auf Daten in einer Instanz steuern, und Instanzen können dies nicht überschreiben. Wenn kein Data Descriptor vorhanden ist, dannbar
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 einbar
vorhanden ist, wird dieser verwendet. Wenn nicht, dann wirIn der Klasse
Foo
nachbar
suchen. Wenn es sich um einen Deskriptor handelt , wird das Deskriptorprotokoll verwendet. So werden Funktionen (in diesem Zusammenhang ungebundene Methoden),classmethod
undstaticmethod
implementiert. Sonst gibt es einfach das Objekt dort zurück oder es gibt einenAttributeError
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:
Die Parameter
first_name
undlast_name
sind jetzt irreführend, da Sie einen vollständigen Namen fürfirst_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.Nicht ganz so wichtig, aber dennoch hervorzuheben: Was ist, wenn
last_name
None
, aberfirst_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:
https://docs.python.org/2/library/functions.html#classmethod
https://docs.python.org/3.5/library/functions.html#classmethod
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.
- Die Klasse besteht aus Attributen (Daten) und Methoden (Funktionen).
- Attribute und Methoden werden einfach als normale Variablen und Funktionen definiert.
- 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. - Attribute, die für die gesamte Klasse gelten, werden zuerst definiert und als Klassenattribute bezeichnet .
- 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). - Jede in der Klassendefinition enthaltene Methode übergibt das betreffende Objekt als ersten Parameter. Das Wort
self
wird für diesen Parameter verwendet (die Verwendung vonself
ist eigentlich eine Konvention, da das Wortself
in Python keine inhärente Bedeutung hat, aber dies ist eine der am meisten respektierten Konventionen von Python, und Sie sollten es immer beachten). - 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 despublic
C ++ / Java-Schlüsselworts nach. Weitere Informationen finden Sie im Beispiel "Private Class Members" auf dieser Seite. - 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