OOP - Attribute

(Grundlegendes)

Die genaue Mechanik von Python Klassen zu verstehen benötigt sicher etwas Zeit. Es müssen die Konzepte der Attribute, der Slots, der Property und der Vererbung verstanden werden.

Hier wollen wir uns erstmal auf Attribute konzentrieren.

Attribute

In Python besitzen Klassen und Instanzen sogenannte Attribute, welche in Datenattribute und Methodenattribute unterschieden werden können.

Datenattribute sind Datentypen die entweder der Klasse oder den Instanzen zugeordnet sind.

Methodenattribute sind Funktion der Teil einer Klasse sind.

Bemerkung

Attribute

Kurzbezeichung

Datenattribute

Attribute

Methodenattribute

Methoden

Klassen

Wir definieren wieder unsere Klasse Dog und wollen untersuchen was Python als Attribute aufgefasst und was nicht.

class Dog:
    species = "pet" # class data attribute
    
    def __init__(self, name): 
        self.name = name # instance data attribute
        
    def speak(self): # instance method attribute
        print(f"{self.name} says wow")
        
paul = Dog('Paul')
print(hasattr(Dog,'species'))
print(hasattr(Dog,'__init__'))
print(hasattr(Dog,'speak'))
print(hasattr(Dog,'name'))
True
True
True
False
print(hasattr(paul,'species'))
print(hasattr(paul,'__init__'))
print(hasattr(paul,'speak'))
print(hasattr(paul,'name'))
True
True
True
True

Das Klassenattribute species ist also ein Attribut der Klasse Dog. Die beiden Instanzmethoden __init__ und speak sind auch Attribute der Klasse Dog. Das erscheint widersprüchlich werden doch beide Methoden als Instanzmethoden bezeichnet.

Jedoch brauchen Instanzmethoden immer Instanzen der Klasse. Die Instanzmethoden selbst sind jedoch Attribute der Klasse. Der Funktionsaufruf sollte das deutlich machen:

Dog.speak(paul)
Paul says wow

Das Ausführen der Methode speak() ist jedoch nicht ohne Instanz möglich. Der Code Dog.speak() würde zum Fehler führen.

# Dog.speak() # errror

Dagegen können wir auf das Klassenattribut species auch ohne Instanz zugreifen.

Dog.species
'pet'

Das Instanzdatenattribute name ist jedoch kein Attribut der Klasse Dog.

# Dog.name # error

Gewisse Attribute einer Klasse können mit __dict__ angezeigt werden.

Dog.__dict__
mappingproxy({'__module__': '__main__',
              'species': 'pet',
              '__init__': <function __main__.Dog.__init__(self, name)>,
              'speak': <function __main__.Dog.speak(self)>,
              '__dict__': <attribute '__dict__' of 'Dog' objects>,
              '__weakref__': <attribute '__weakref__' of 'Dog' objects>,
              '__doc__': None})

Allerding zeigt nur dir() alle Attribute einer Klasse an.

dir(Dog)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'speak',
 'species']

Auch hier können wir überprüfen ob name ein Attribute der Klasse Dog ist.

'name' in dir(Dog)
False

Wie wir hier sehen sind die Attribute von __dict__ eine Untermenge von dir().

# change list1 and list2 to sets, then check if set1 is a subset of set2
def sublist(lst1, lst2):
    return set(lst1) <= set(lst2)

sublist(list(Dog.__dict__.keys()),dir(Dog))
True

Bemerkung

Als Instanzattribut werden Daten- und Methodenattribute bezeichnet, deren Verwendung eine Instanz benötigt. Dagegen können Klassenattribute ohne Instanzen verwenden werden. Die Python Funktion hasattr liefert leider andere Ergebnisse als der Sprachgebrauch von Python. Das liegt daran, dass hasattr untersucht ob das Objekt bereits das Attribut.

help(hasattr)
Help on built-in function hasattr in module builtins:

hasattr(obj, name, /)
    Return whether the object has an attribute with the given name.
    
    This is done by calling getattr(obj, name) and catching AttributeError.

Instanz

Wir wollen nun eine Instanz d der Klasse Dog untersuchen.

d = Dog('Max')
d.speak()
Max says wow
print(hasattr(d,'species'))
print(hasattr(d,'__init__'))
print(hasattr(d,'speak'))
print(hasattr(d,'name'))
True
True
True
True

Hier sind nun alle auch Attribute der Instanz.

d.__dict__
{'name': 'Max'}
dir(d)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'name',
 'speak',
 'species']
sublist(list(d.__dict__.keys()),dir(d))
True
set(d.__dict__.keys())
{'name'}
set(dir(d))
{'__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'name',
 'speak',
 'species'}
set(d.__dict__.keys()).issubset(set(dir(d)))
True

Verwalten von Attributen

Methode

Beschreibung

hasattr()

-

setattr()

-

getattr()

-

delattr()

-

class Dog:
    def __init__(self, name): 
        self.name = name # instance data attribute

Lesen und Schreiben von Attributen

d = Dog('Max')
d.name
'Max'
setattr(d,'name','Moritz')
getattr(d,'name')
'Moritz'

Hinzufügen und Entfernen Attributen

d = Dog('Max')
d.__dict__
{'name': 'Max'}
d.age = 20
d.__dict__
{'name': 'Max', 'age': 20}
delattr(d,'age')
d.__dict__
{'name': 'Max'}

Instanz, Klassen und statische Methoden

class TestClass:
    def method(self): # instance method
        print(self)
    
    @classmethod
    def classmethod(cls): # class method
        print(cls)
        
    @staticmethod
    def staticmethod(): # static method
        print("Doing something!")
t = TestClass()
t.method()
<__main__.TestClass object at 0x7f7c58541bb0>
TestClass.classmethod()
<class '__main__.TestClass'>
TestClass.staticmethod()
Doing something!

object - Alles ist ein Objekt

In Python ist alles ein Objekt. Ein Objekt ist eine Instanz einer Klasse. Jede Klasse ist von der Python Klasse object abgleitet.

object
object
dir(object)
['__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']
def sublist(lst1, lst2):
    return set(lst1) <= set(lst2)

sublist(list(object.__dict__.keys()),dir(object))
True

Im Folgedem wollen wir zeigen das alle Datentypen eine Instanz von der object Klasse ist.

var=None
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var=1
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var=1.0
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var="String"
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var=['a','b']
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var=('a','b')
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var={'a','b'}
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
var={'a':1,'b':2}
print("Der Datentyp "+str(type(var))+" ist eine Instanz der Klasse "+ str(type(object))+" :",isinstance(var,object))
Der Datentyp <class 'NoneType'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'int'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'float'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'str'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'list'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'tuple'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'set'> ist eine Instanz der Klasse <class 'type'> : True
Der Datentyp <class 'dict'> ist eine Instanz der Klasse <class 'type'> : True

Wie wir sehen ist in Python alles ein Objekt.

var=object
print(isinstance(var,object))
True

Selbst ein Objekt ist eine Instanz eines Objektes.

Fortgeschrittene Techniken

Tipp

Für weitere Methoden sei auf Objektorientierte Programmierung im Abschnitt Fortgeschrittene verwiesen.