UML diagrams in python? - python

I just got started learning about UML and I am currently trying to implement functionality for this diagram using python.
I have wrote the inheritance part, but I don't know yet how to deal with the association part of it. Could anyone give me some pointers on how to do this? Especially the circular reference.
My implementation so far:
class RailCar():
def __init__(self, name):
if len(name) >= 2:
self.__name = name
self.locomotive = None
else:
raise Exception("Name length of", type(self), " object must be at least 2 characters long.")
class PassengerCar(RailCar):
def __init__(self, capacity):
super().__init__(self, name)
self.__capacity = capacity
class RestaurantCar(RailCar):
def __init__(self, first):
super().__init__(self, name)
self.__first = first
class Locomotive():
def __init__(self, railCar):
self.railCar = railCar
rc01 = RailCar('01')
rc01.locomotive = "It works!!"
print(rc01.locomotive)

Supposedly the self reference is a doppelganger of the private next attribute. It should rather be written with the role name next to the association like so:
Anyhow, you would code it with self._next holding a reference to a RailCar instance. The getter/setter would then reference this private property.
class RailCar():
def __init__(self, name):
# as above
self._next = None
def setNext(self, next):
self._next = next
# you might check if the instance is RailCar.
#property
def getNext(self):
return self._next
P.S. The Locomotive-RailCar relation would be similar (with the same modeling issue). Just a self._first in Locomotive.

Related

How to access a subclass method from parent method?

I’m trying to call a method found within a subclass from the class it inherits from.
class Account:
def __init__(self, full_name):
self.full_name = full_name
class Transactions(Account):
def __init__(self, full_name, amount=0):
super().__init__(full_name)
self._transactions = []
def add_transaction(self, amount):
if not isinstance(amount, int):
return ValueError('Please use an int.')
self._transactions.append(amount)
acc_0 = Account('Forest Whitaker')
I want to call the function ‘add_transaction()’ using acc_0’s info. Not sure if I’m overthinking but how would I go about this?
sidenote: if anyone is familiar with rbx.lua, in this situation I’d be trying to do something like this: acc_0.Transactions.add_transaction(50)
Since you are instantiating an Account, you can't access any transactions, because that class doesn't implement that functionality. You would need to instantiate the subclass Transactions, eg:
acc_0 = Transactions('Forest Whitaker', 9)
There are actually several problems with your code above, but the subclassing isn't really right for this job anyway.. it's a classic "is a" vs "has a" object oriented problem: Do transactions HAVE an account, not really.. Is transactions a type of account?? No, not right either. But, does an account HAVE transactions? Yes, it does. So an instance of Transactions should be a member of Account. So:
class Account:
def __init__(self, full_name):
self.full_name = full_name
self.transactions = Transactions()
class Transactions:
def __init__(self):
super().__init__()
self._transactions = []
def add_transaction(self, amount):
if not isinstance(amount, int):
return ValueError('Please use an int.')
self._transactions.append(amount)
acc_0 = Account('Forest Whitaker')
acc_0.transactions.add_transaction(9)
The inheritance hierarchy is wrong looking at what you are trying to achieve. That should be other way around.
class Transactions:
def __init__(self):
self._transactions = []
def add_transaction(self, amount):
if not isinstance(amount, int):
return ValueError('Please use an int.')
self._transactions.append(amount)
class Account(Transactions):
def __init__(self, full_name):
super().__init__()
self.full_name = full_name
acc_0 = Account('Forest Whitaker')
# now you can call
acc_0.add_transaction(10)

What is the difference between readable property method and a callable function that is just returns the data as a property can?

I have a property that returns list of names with "ash" in it
class BaseClass(object):
def __init__(self):
self.filter_key = ""
self.name = ""
def filter_names(self, filter_key):
self.filter_key = filter_key
#property
def student_names(self):
return self.names
def callable_function_names(self):
return names
and then student class that inherits BaseClass
class StudentClass(BaseClass):
#property
def student_names(self):
names = super(StudentClass, self).student_names
return [name for name in names if self.filter_students in name]
#property
def filter_key(self):
"""Gets """
return self.filter_key
#slot_key.setter
def filter_key(self, key):
"""Sets name filter"""
self.filter_names(key)
# or by doing :
def callable_function_names(self):
names = super(StudentClass, self).callable_function_names()
return [name for name in names if self.filter_students in name]
So if I create obj of the student class.
studentclsObj = StudentClass()
studentclsObj.filter_key = "ash"
print studentclsObj.student_names
print studentclsObj.callable_function_names()
I can achieve the same result with both above prints, is there any difference and what is preferred and right way to do ?
One use case of properties is not breaking API. This is one of main strengths of python IMO. You can take a function, make transform it in a callable object, add new functionality without breaking old code, now the property
I see three main uses of properties over attributes,
Read only attributes
Is easy to create read only attributes with properties. They are non verbose, self documenting and simple
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
Validation on writable properties
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, val):
if valid(val):
self._bar = val
This is a kind of defensive programming
Keep API compatibility
Imagine that you have a class for a bank account, with
a balance property
class BankAccount:
def __init__(self):
self.balance = 0
You have this code and it works fine. But know your client
says, I need you to log every balance lookup. You can replace
the attribute by a property without breaking old code
class BankAccount:
def __init__(self):
self._balance = 0
#property
def balance(self):
self.log_balance_read()
return self._balance
There is no difference between a property and a method which return the same value. Go for the simpler, use method for actions and state changes and attributes for real attributes, if you need to add logic to attribute lookup, python will let you do it

How to avoid repeating self when defining subclasses in python?

I'm trying to build an algorithm which constructs funny but grammatical sentences. As a part of this, I need to classify words into different categories, and want to explore using OOP to achieve this, but I am struggling to avoid violating DRY. Below is a reduced example.
class word:
def __init__(self,name):
self.name = name
self.firstLetter = self.name[0]
self.length = len(name)
class noun(word):
def __init__(self, name):
self.name=name
super().__init__(name)
class agent(noun):
def __init__(self, name):
self.name=name
super().__init__(name)
I would like all words to share certein methods and properties, e.g. having a "length" equal to the length of their name. However, I only need an object to be named at the very lowest level (in this case agent), and thus it seems superflous to feature a name in the init of each subclass.
In summary, is there any way of acheive the same end result of the above code without having to write the following ad nauseam?
def __init__(self, name):
self.name=name
super().__init__(name)
The following dryes up your code:
class word:
firstLetter: str = ''
def __init__(self, name: str) -> None:
self.name = name
self.length: int = len(name)
if self.length:
self.firstLetter = self.name[0]
class noun(word):
pass
class agent(noun):
pass

Python - inner class is not defined?

I have to do an unrolled linked list for one of my classes. I'm new to python, but not to programming, and for some reason I cannot get around this little problem!
I have a class Node that is to be the node object used within the unrolled linked list. The unrolled linked list class performs all the operations on the Node class.
class UnrolledLinkedList(object):
""" INNER NODE CLASS """
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
""" END NODE CLASS """
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
""" OTHER FUNCTIONS OF UNROLLEDLINKEDLIST CLASS """
The problem comes at the last line of the UnrolledLinkedList class' init function: "global name Node is not defined". I double checked my indentation and looked all over the internet for examples of something like this, but couldn't find any. Would someone mind explaining to me what's wrong?
Methods do not include their class as a scope to be searched. If you want this to work then you will need to use either UnrolledLinkedList.Node or self.Node instead.
The inner class Node is a member of the class UnrolledLinkedList and can only be accessed via self.
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Use:
self.head = self.Node()
and it works.
A class does not create its own name space. Using self.Node(), Python first searches all attributes of the instances. Since it does not find the name Node there, it it searches the class UnrolledLinkedList for Node.
Alternatively, you can use the class name directly:
UnrolledLinkedList.Node()
You can achieve the same without nesting the class Node:
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
class UnrolledLinkedList(object):
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
Qualify Node() with self:
class UnrolledLinkedList(object):
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Python needs to qualify references to things. In this case, you could either say UnrolledLinkedList.Node() or self.Node().

Python: Instance class mutation

I have two classes:
class Egg:
def __init__(self):
self._color = 'White'
class Larvae(Egg):
def __init__(self):
super().__init__()
self._color = 'Transparente'
To illustrate ...
So, in my code I have an Egg instance. When it's time I would like transform them into Larvae instances. I can create a Larvae instance and hand copy informations about a "previous" Egg instance. What does Python offer for something like that? For "mute" an instance in instance of subclass of its class?
Edit: As commented below, OOP in this question is not good way to do the wanted behavior. So, keep this in mind when reading answer
How about using a state-based approach?
class Ant:
def __init__(self, state='egg'):
self.state = state
#property
def color(self):
return {
'egg': 'Transparent',
'larvae': 'White,'
}[self.state]
def hatch(self):
if self.state == 'egg':
self.state = 'larvae'
else:
raise Exception('Cannot hatch if not an egg!')
This is possible, but not a great idea.
It is possible to change the type of an object after constructing it, but, well, it's just not robust. When you change the type of an object, its __init__() does not run. Any attributes left over from the old type are still there, and if they clash with attributes belonging to the new type, you probably have a nasty mess to deal with. If you're really sure this is the right approach, you can do it by assigning to the __class__ attribute (e.g. spam.__class__ = SomeClass). I strongly advise against this, however.
Instead, I would recommend factoring out the data you want to preserve into a "state" attribute, which you then transfer from one type to another. For example:
class Egg(object):
def __init__(self, state):
# Other egg-related stuff here...
self.state = state
def grow_up(self):
return Larva(self.state)
class Larva(object):
def __init__(self, state):
# Other larva-related stuff here...
self.state = state
def grow_up(self):
return Pupa(self.state)
class Pupa(object):
# and so on...
spam = Egg([1, 2, 3])
spam = spam.grow_up()
print(spam.state) # [1, 2, 3]
This answer has little to do with your request, but I find that it's worth to show that your OOP is a little bit counterintuitive. The whole point of OOP is that you create an intuitive inheritance hierarchy. There is little point in OO if your low level classes are supposed to be aware of higher level ones. In your use-case the Egg must be aware of the Larvae in order to transform, but that makes little sense. A parent class should not reference its child-classes.
class BaseInsect(object):
"""
Some stuff that all insects share at any stage
"""
def __init__(self, colour, *args, **kwargs):
self.colour = colour
...
class ImmatureAnt(BaseInsect):
...
class Egg(ImmatureAnt):
"""
Some egg-specific stuff
"""
def __init__(self, *args, **kwargs):
super(Egg, self).__init__("white")
...
class Larvae(ImmatureAnt):
def __init__(self, *args, **kwargs):
super(Larvae, self).__init__("transparent")
...
#staticmethod
def from_egg(egg, *args, **kwargs):
# make a larvae out of an egg
...
class BaseAdultAnt(BaseInsect):
"""
Some stuff all adult ants have
"""
...
class WorkerAnt(BaseAdultAnt):
...
class BaseReproducingAnt(BaseAdultAnt):
...
class Male(BaseReproducingAnt):
...
class Queen(BaseReproducingAnt):
...
Back to your question.
You've already been told that you can pass an Egg instance to the Larvae constructor. That will be a beautiful way. To make this answer a little bit less off-topic I'm giving an alternative solution. You might want to use several functions.
def egg_to_larvae(egg, *args, **kwargs):
"""
:param egg: an Egg instance
:type egg: Egg
"""
# do some stuff to get all the info needed to create a larvae...
return larvae
def larvae_to_pupae(larvae, *args, **kwargs):
...
return pupae
You get the idea
Just use a common base class instead of trying to inherit Larvae from an Egg:
class AntBase:
def __init__(self, color):
self._color = color
class Egg(AntBase):
def __init__(self, color='White'):
super().__init__(color)
class Larvae(AntBase):
def __init__(self, color='Transparente'):
super().__init__(color)
While Egg and Larvae are similar (both related to ants), Larvae is not an Egg, nor is an Egg a Larvae.
They are both "ant-things" though, thus we create a common AntBase class for all your ant things.
To convert an Egg into a Larvae, you can have a converter classmethod:
class AntBase:
def __init__(self, color):
self._color = color
#classmethod
def transform_from(cls, instance):
return cls(instance._color)
And now you can do:
egg0 = Egg()
larvae0 = Larvae.transform_from(egg0)
You can even have custom behaviour for each class:
class Larvae(AntBase):
#classmethod
def transform_from(cls, instance):
if isinstance(instance, AntMale):
raise ValueError("You can't have Larvae from AntMale")
return super().transform_from(instance)

Categories

Resources