Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> Python

Python Object-Georiënteerd Programmeren meesteren: Een uitgebreide gids

Python is een object-georiënteerde taal sinds het bestond. Hierdoor is het aanmaken en gebruiken van klassen en objecten heel eenvoudig. Dit hoofdstuk helpt je een expert te worden in het gebruik van Python's object-georiënteerde programmeerondersteuning.

Als je geen eerdere ervaring hebt met object-georiënteerd (OO) programmeren, wil je misschien een introductorisch cursus raadplegen of ten minste een tutorial van een of andere soort zodat je de basisconcepten begrijpt.

Hier is echter een kleine introductie van Object-Georiënteerd Programmeren (OOP) om je op snelheid te brengen:

Overzicht van OOP-terminologie

  • Klasse − Een door de gebruiker gedefinieerd prototype voor een object dat een set attributen definieert die elk object van de klasse karakteriseren. De attributen zijn dataleden (klassevariabelen en instantievariabelen) en methoden, toegankelijk via puntnotatie.

  • Klassevariabele − Een variabele die gedeeld wordt door alle instanties van een klasse. Klassevariabelen worden gedefinieerd binnen een klasse maar buiten elke van de klassemethoden. Klassevariabelen worden niet zo frequent gebruikt als instantievariabelen.

  • Dataleden − Een klassevariabele of instantievariabele die data bevat geassocieerd met een klasse en zijn objecten.

  • Functie-overloading − De toewijzing van meer dan één gedrag aan een bepaalde functie. De uitgevoerde operatie varieert op basis van de typen objecten of argumenten betrokken.

  • Instantievariabele − Een variabele die binnen een methode gedefinieerd is en alleen behoort tot de huidige instantie van een klasse.

  • Erfelijkheid − De overdracht van de kenmerken van een klasse aan andere klassen die ervan afgeleid zijn.

  • Instantie − Een individueel object van een bepaalde klasse. Een object obj dat behoort tot een klasse Circle, is bijvoorbeeld een instantie van de klasse Circle.

  • Instantiatie − De creatie van een instantie van een klasse.

  • Methode − Een speciaal soort functie die in een klassedefinitie gedefinieerd is.

  • Object − Een unieke instantie van een datastructuur die gedefinieerd wordt door zijn klasse. Een object omvat zowel dataleden (klassevariabelen en instantievariabelen) als methoden.

  • Operator-overloading − De toewijzing van meer dan één functie aan een bepaalde operator.

Klassen aanmaken

De class-instructie creëert een nieuwe klassedefinitie. De naam van de klasse volgt onmiddellijk op het sleutelwoord class gevolgd door een dubbele punt als volgt:

class Klassenaam:
    'Optionele klassedocumentatiestring'
    class_suite
  • De klasse heeft een documentatiestring, die toegankelijk is via Klassenaam.__doc__.

  • De class_suite bestaat uit alle component-instructies die klassedefinities, data-attributen en functies definiëren.

Voorbeeld

Hieronder volgt het voorbeeld van een eenvoudige Python-klasse:

class Werknemer:
    'Gemeenschappelijke basisklasse voor alle werknemers'
    aantal_werknemers = 0
    def __init__(self, naam, salaris):
        self.naam = naam
        self.salaris = salaris
        Werknemer.aantal_werknemers += 1
    def toonAantal(self):
        print("Totaal Werknemer %d" % Werknemer.aantal_werknemers)
    def toonWerknemer(self):
        print("Naam : ", self.naam, ", Salaris: ", self.salaris)
  • De variabele aantal_werknemers is een klassevariabele wiens waarde gedeeld wordt onder alle instanties van deze klasse. Dit kan benaderd worden als Werknemer.aantal_werknemers van binnen de klasse of buiten de klasse.

  • De eerste methode __init__() is een speciale methode, die klasse-constructor of initialisatiemethode genoemd wordt die Python aanroept wanneer je een nieuwe instantie van deze klasse aanmaakt.

  • Je declareert andere klassemethoden als normale functies met de uitzondering dat het eerste argument aan elke methode self is. Python voegt het self-argument toe aan de lijst voor je; je hoeft het niet op te nemen wanneer je de methoden aanroept.

Instantie-objecten aanmaken

Om instanties van een klasse aan te maken, roep je de klasse aan met de klassenaam en geef je de argumenten door die de __init__-methode accepteert.

"Dit zou het eerste object van de Werknemer-klasse aanmaken"
emp1 = Werknemer("Zara", 2000)
"Dit zou het tweede object van de Werknemer-klasse aanmaken"
emp2 = Werknemer("Manni", 5000)

Attributen benaderen

Je benadert de attributen van het object met behulp van de puntoperator met het object. Klassevariabele zou benaderd worden met behulp van de klassenaam als volgt:

emp1.toonWerknemer()
emp2.toonWerknemer()
print("Totaal Werknemer %d" % Werknemer.aantal_werknemers)

Nu, alle concepten samen:

#!/usr/bin/python
class Werknemer:
    'Gemeenschappelijke basisklasse voor alle werknemers'
    aantal_werknemers = 0
    def __init__(self, naam, salaris):
        self.naam = naam
        self.salaris = salaris
        Werknemer.aantal_werknemers += 1
    def toonAantal(self):
        print("Totaal Werknemer %d" % Werknemer.aantal_werknemers)
    def toonWerknemer(self):
        print("Naam : ", self.naam, ", Salaris: ", self.salaris)

"Dit zou het eerste object van de Werknemer-klasse aanmaken"
emp1 = Werknemer("Zara", 2000)
"Dit zou het tweede object van de Werknemer-klasse aanmaken"
emp2 = Werknemer("Manni", 5000)
emp1.toonWerknemer()
emp2.toonWerknemer()
print("Totaal Werknemer %d" % Werknemer.aantal_werknemers)

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

Naam : Zara ,Salaris: 2000
Naam : Manni ,Salaris: 5000
Totaal Werknemer 2

Je kunt attributen van klassen en objecten op elk moment toevoegen, verwijderen of wijzigen:

emp1.leeftijd = 7  # Voeg een 'leeftijd'-attribuut toe.
emp1.leeftijd = 8  # Wijzig 'leeftijd'-attribuut.
del emp1.leeftijd  # Verwijder 'leeftijd'-attribuut.

In plaats van de normale instructies te gebruiken om attributen te benaderen, kun je de volgende functies gebruiken:

  • De getattr(obj, naam[, standaard]) − om het attribuut van het object te benaderen.

  • De hasattr(obj,naam) − om te controleren of een attribuut bestaat of niet.

  • De setattr(obj,naam,waarde) − om een attribuut in te stellen. Als het attribuut niet bestaat, wordt het aangemaakt.

  • De delattr(obj, naam) − om een attribuut te verwijderen.

hasattr(emp1, 'leeftijd')  # Retourneert true als 'leeftijd'-attribuut bestaat
getattr(emp1, 'leeftijd')  # Retourneert waarde van 'leeftijd'-attribuut
setattr(emp1, 'leeftijd', 8)  # Stel attribuut 'leeftijd' in op 8
delattr(emp1, 'leeftijd')  # Verwijder attribuut 'leeftijd'

Ingebouwde klasse-attributen

Elke Python-klasse houdt de volgende ingebouwde attributen bij en ze kunnen benaderd worden met de puntoperator zoals elk ander attribuut:

  • __dict__ − Woordenboek met de naamruimte van de klasse.

  • __doc__ − Klassedocumentatiestring of none, als niet gedefinieerd.

  • __name__ − Klassenaam.

  • __module__ − Modulenaam waarin de klasse gedefinieerd is. Dit attribuut is "__main__" in interactieve modus.

  • __bases__ − Een mogelijkerweise lege tuple met de basisklassen, in de volgorde van hun voorkomen in de basisklassenlijst.

Voor de bovenstaande klasse laten we al deze attributen benaderen:

#!/usr/bin/python
class Werknemer:
    'Gemeenschappelijke basisklasse voor alle werknemers'
    aantal_werknemers = 0
    def __init__(self, naam, salaris):
        self.naam = naam
        self.salaris = salaris
        Werknemer.aantal_werknemers += 1
    def toonAantal(self):
        print("Totaal Werknemer %d" % Werknemer.aantal_werknemers)
    def toonWerknemer(self):
        print("Naam : ", self.naam, ", Salaris: ", self.salaris)

print("Werknemer.__doc__:", Werknemer.__doc__)
print("Werknemer.__name__:", Werknemer.__name__)
print("Werknemer.__module__:", Werknemer.__module__)
print("Werknemer.__bases__:", Werknemer.__bases__)
print("Werknemer.__dict__:", Werknemer.__dict__)

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

Werknemer.__doc__: Gemeenschappelijke basisklasse voor alle werknemers
Werknemer.__name__: Werknemer
Werknemer.__module__: __main__
Werknemer.__bases__: ()
Werknemer.__dict__: {'__module__': '__main__', 'toonAantal': , 'aantal_werknemers': 2, 'toonWerknemer': , '__doc__': 'Gemeenschappelijke basisklasse voor alle werknemers', '__init__': }

Objecten vernietigen (Garbage Collection)

Python verwijdert onnodige objecten (ingebouwde typen of klasse-instanties) automatisch om geheugeneruimte vrij te maken. Het proces waarbij Python periodiek geheugenblokken terugvind die niet langer in gebruik zijn, heet Garbage Collection.

Python's garbage collector loopt tijdens de programma-uitvoering en wordt getriggerd wanneer het referentietelling van een object nul bereikt. Het referentietelling van een object verandert naarmate het aantal aliassen dat ernaar wijst verandert.

Het referentietelling van een object neemt toe wanneer het een nieuwe naam toegewezen krijgt of in een container (lijst, tuple of woordenboek) geplaatst wordt. Het referentietelling van het object neemt af wanneer het met del verwijderd wordt, de referentie hertoegewezen wordt of de referentie buiten bereik gaat. Wanneer het referentietelling van een object nul bereikt, verzamelt Python het automatisch.

a = 40  # Maak object <40>
b = a   # Verhoog ref. telling van <40>
c = [b] # Verhoog ref. telling van <40>
del a   # Verminder ref. telling van <40>
b = 100 # Verminder ref. telling van <40>
c[0] = -1 # Verminder ref. telling van <40>

Je zal normaal niet merken wanneer de garbage collector een wezenloze instantie vernietigt en de ruimte terugvindt. Maar een klasse kan de speciale methode __del__() implementeren, een destructor genoemd, die aangeroepen wordt wanneer de instantie op het punt staat te worden vernietigd. Deze methode kan gebruikt worden om niet-geheugenbronnen op te schonen die door een instantie gebruikt worden.

Voorbeeld

Deze __del__()-destructor print de klassenaam van een instantie die op het punt staat te worden vernietigd:

#!/usr/bin/python
class Punt:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    def __del__(self):
        klassenaam = self.__class__.__name__
        print(klassenaam, "vernietigd")

pt1 = Punt()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3))  # print de ids van de objecten
del pt1
del pt2
del pt3

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

3083401324 3083401324 3083401324
Punt vernietigd

Let op − Ideaal definieer je je klassen in een apart bestand, dan importeer je ze in je hoofdprogrammabestand met de import-instructie.

Klasse-erven

In plaats van vanaf nul te beginnen, kun je een klasse aanmaken door het af te leiden van een vooraf bestaande klasse door de basisklasse tussen haakjes na de nieuwe klassenaam op te geven.

De kindklasse erft de attributen van de ouderklasse, en je kunt die attributen gebruiken alsof ze in de kindklasse gedefinieerd waren. Een kindklasse kan ook dataleden en methoden van de ouder overschrijven.

Syntaxis

Afgeleide klassen worden gedeclareerd zoals hun ouderklasse; echter, een lijst met basisklassen om van te erven wordt na de klassenaam gegeven:

class SubKlasNaam (OuderKlas1[, OuderKlas2, ...]):
    'Optionele klassedocumentatiestring'
    class_suite

Voorbeeld

#!/usr/bin/python
class Ouder:
    # definieer ouderklasse
    ouderAttr = 100
    def __init__(self):
        print("Ouder constructor aanroepen")
    def ouderMethode(self):
        print('Ouder methode aanroepen')
    def setAttr(self, attr):
        Ouder.ouderAttr = attr
    def getAttr(self):
        print("Ouder attribuut :", Ouder.ouderAttr)

class Kind(Ouder):
    # definieer kindklasse
    def __init__(self):
        print("Kind constructor aanroepen")
    def kindMethode(self):
        print('Kind methode aanroepen')

c = Kind()  # instantie van kind
c.kindMethode()  # kind roept zijn methode aan
c.ouderMethode()  # roept ouders methode aan
c.setAttr(200)  # weer ouders methode aanroepen
c.getAttr()  # weer ouders methode aanroepen

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

Kind constructor aanroepen
Kind methode aanroepen
Ouder methode aanroepen
Ouder attribuut : 200

Op dezelfde manier kun je een klasse van meerdere ouderklassen afleiden als volgt:

class A:
    # definieer je klasse A .....
class B:
    # definieer je klasse B .....
class C(A, B):
    # subklasse van A en B .....

Je kunt issubclass() of isinstance() functies gebruiken om een relatie van twee klassen en instanties te controleren.

  • De issubclass(sub, sup) booleaanse functie retourneert true als de gegeven subklasse sub inderdaad een subklasse is van de superklasse sup.

  • De isinstance(obj, Klas) booleaanse functie retourneert true als obj een instantie is van klasse Klas of een instantie is van een subklasse van Klas.

Methoden overschrijven

Je kunt altijd de methoden van je ouderklasse overschrijven. Een reden voor het overschrijven van de methoden van de ouder is dat je misschien speciale of andere functionaliteit in je subklasse wilt.

Voorbeeld

#!/usr/bin/python
class Ouder:
    # definieer ouderklasse
    def mijnMethode(self):
        print('Ouder methode aanroepen')

class Kind(Ouder):
    # definieer kindklasse
    def mijnMethode(self):
        print('Kind methode aanroepen')

c = Kind()  # instantie van kind
c.mijnMethode()  # kind roept overschreven methode aan

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

Kind methode aanroepen

Basis overloading methoden

De volgende tabel lijst een aantal generieke functionaliteit die je in je eigen klassen kunt overschrijven:

Nr. Methode, Beschrijving & Voorbeeldoproep
1

__init__ ( self [,args...] )

Constructor (met eventuele optionele argumenten)

Voorbeeldoproep : obj = klasNaam(args)

2

__del__( self )

Destructor, verwijdert een object

Voorbeeldoproep : del obj

3

__repr__( self )

Evalueerbare string representatie

Voorbeeldoproep : repr(obj)

4

__str__( self )

Printbare string representatie

Voorbeeldoproep : str(obj)

5

__cmp__ ( self, x )

Objectvergelijking

Voorbeeldoproep : cmp(obj, x)

Operatoren overladen

Stel je hebt een Vector-klasse aangemaakt om twee-dimensionale vectoren weer te geven, wat gebeurt er als je de plus-operator gebruikt om ze op te tellen? Waarschijnlijk zal Python bij je schreeuwen.

Je kunt echter de __add__-methode in je klasse definiëren om vectoroptelling uit te voeren en dan zou de plus-operator zich gedragen zoals verwacht:

Voorbeeld

#!/usr/bin/python
class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __str__(self):
        return 'Vector (%d, %d)' % (self.a, self.b)
    def __add__(self, other):
        return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print(v1 + v2)

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

Vector(7,8)

Data verbergen

Een object's attributen mogen of mogen niet zichtbaar zijn buiten de klassedefinitie. Je moet attributen benoemen met een dubbele underscore-prefix, en die attributen zijn dan niet direct zichtbaar voor buitenstanders.

Voorbeeld

#!/usr/bin/python
class AlleenTeller:
    __geheimeTeller = 0
    def tel(self):
        self.__geheimeTeller += 1
        print(self.__geheimeTeller)

teller = AlleenTeller()
teller.tel()
teller.tel()
print(teller.__geheimeTeller)

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

1
2
Traceback (most recent call last):
  File "test.py", line 12, in 
    print(teller.__geheimeTeller)
AttributeError: AlleenTeller instantie heeft geen attribuut '__geheimeTeller'

Python beschermt die leden door de naam intern te wijzigen om de klassenaam op te nemen. Je kunt zulke attributen benaderen als object._klasNaam__attribuutNaam. Als je je laatste regel vervangt door het volgende, dan werkt het voor je:

.........................
print(teller._AlleenTeller__geheimeTeller)

Wanneer de bovenstaande code wordt uitgevoerd, produceert dit het volgende resultaat:

1
2
2

Python

  1. Geautomatiseerde Visuele Object-tracking met Raspberry Pi en OpenCV
  2. C# Classes & Objects: Fundamenten voor Robuuste OOP
  3. Python Datatypes onder de knie krijgen: Een praktische gids
  4. Python Operators onder de knie krijgen: Een uitgebreide gids
  5. Python Pass Statement onder de knie krijgen: Een praktische gids
  6. Python-dictionaries beheersen: aanmaken, manipuleren en geavanceerde technieken
  7. Python-custom exceptions: een praktische gids
  8. Python Object-Oriented Programming beheersen: classes, inheritance, encapsulatie & polymorfisme
  9. Python-erfelijkheid beheersen: concepten, syntax en praktische voorbeelden
  10. type() en isinstance() in Python begrijpen: praktische voorbeelden
  11. Java Singleton patroon uitgelegd: hoe veilig te implementeren en gebruiken