How to access a subclass method from parent method? - python

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)

Related

UML diagrams in 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.

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

Automatic inheritance of all base class attributes

I want to create a class that has two characteristics:
Inherits a base class (all attributes and methods) automatically
Takes an object of that base class as an argument.
I want these two characteristics because I want to automatically inherit all the attributes and methods of the previous object (base class object) without having to do something like use the __init__ method since this will cause recalculation of the already computed initialization. And since there will be a lot of methods and attributes I don't think its good practice to do it manually.
My idea of the code would look something like this.
class BaseClass(object):
def __init__(self, name, date):
self.name = name
self.date = date
def get_name_date(self):
self.name_date = self.name +self.date
class UpperClass(BaseClass):
def __init__(self):
self.date_name = self.date + self.name
I know the code above will not work and I dont want to do something like:
class UpperClass(BaseClass):
def __init__(self):
super(BaseClass, self).__init__(name, date)
self.date_name = self.date + self.name
Cause this will re-do calculations I already have.
Maybe inheritance is not what I'm looking for, any pointers?
Is this what you are looking for?
class BaseClass(object):
def __init__(self, name, date):
self.name = name
self.date = date
def get_name_date(self):
self.name_date = self.name +self.date
class UpperClass:
def __init__(self, baseobject):
self.baseobject = baseobject
self.date_name = baseobject.date + baseobject.name
def __getattr__(self, item):
return getattr(self.baseobject, item)
o1 = BaseClass('thmei', 'may')
o2 = UpperClass(o1)
print(o1.date) # may
print(o2.date) # may
print(o2.date_name) # maythmei

Combining dict in super class's init and subclass's init automatically?

I'm creating an event system which uses the following class for events:
class Event(set):
def __init__(self, name, iterable=()):
super().__init__(iterable)
self.name = name
def __iadd__(self, listener):
self.add(listener)
return self
def __isub__(self, listener):
self.remove(listener)
return self
def fire(self, **eargs):
for listener in self:
listener(**eargs)
Now I'm trying to create some kind of a dict that would automatically create the events in its __init__ like so:
class EventDict(dict):
def __init__(self, prefix, *event_names):
super().__init__({
name: Event('%s.%s' % (prefix, name))
for name in event_names
})
And here's an example of usage:
class Player:
def __init__(self, name):
self._name = name
self.events = EventDict('Player', 'change_name')
#property
def name(self):
returns self._name
#name.setter
def name(self, value):
old_name = self.name
self.name = value
self.events['change_name'].fire(player=self, old_name=old_name)
Now the problem I'm facing is subclassing.
If I were to subclass my Player class to include also health attribute, I can't use the same way of creating an event dict, cause it would override the existing one and I couldn't access change_name anymore.
So I'm trying to find a way where I can just do something like this (ideal solution):
class Player:
events = EventDict('Player', 'change_name')
class Player2(Player):
events = EventDict('Player2', 'attack', 'kill')
p2 = Player2()
p2.events['change_name'] += my_event_listener # Still access Player class's events
Would something like this be possible?
I know I can do:
class Player2(Player):
def __init__(self, name):
super().__init__()
self.events.update(...)
But it's not the same :P
I think what you want is:
class Player:
EVENTS = ('change_name',)
def __init__(self, name):
self._name = name
self.events = EventDict(
self.__class__.__name__,
*self.EVENTS,
)
...
Then all you need in Player2 is:
class Player2(Player):
EVENTS = Player.EVENTS + ('attack', 'kill')
and the inherited __init__ will work fine.
Stop using EventDict.
The class itself has its own dict which supports inheritance like that.
class Player:
def __init__(self, name):
self._name = name
self.change_name_event = Event('Player.change_name')
class Player2(Player):
def __init__(self, name):
super().__init__(name)
self.attack_event = Event('Player2.attack')
self.kill_event = Event('Player2.kill')
All the events from the subclasses will be added no matter what.
I noticed that maybe you wanted to make it obvious that they're events, so I added 'event' to the names of the fields, but you don't need to if you don't want to.
If you wanted it so that the prefix is the same throughout, then you'd change the strings from something like 'Player.change_name' to self.__class__.__name__ + '.change_name'. That way, it always gets whatever the actual class for the object is. This is part of what #jonrsharpe's solution is trying to get at.
If you wanted to make it so others could add more events dynamically, they can simply do a line like playerObj.my_new_event = Event('Player.my_new_event') or you could provide a nice method in the Player class to make their lives easier:
def add_event(self, event_name):
setattr(self, event_name, Event(self.__class__.__name__ + '.' + event_name)

how a class works

I am trying to figure out a really simple problem but still I can't quite get how a class works. For example, in case I wanted to create a class called "Friend" with an attribute called "name", does this mean I will have to give a variable called "name"before anything else ? Then how can i define the constructor to allow the specification of "name"? Is this code nonsense? Thanks in advance for any response
class Friend:
def __init__(self,name):
self.set_name(name)
def set_name(self,name):
self.name=name
def get_name(self):
return self.name
That code is not nonsense as in it accomplishes what you want to accomplish. It is not very pythonic, though. There are no reason you should use getter or setters. Just access the attributes directly. Like
class Friend:
def __init__(self,name):
self.name = name
you can instantiate your class by
friend = Friend('jeremy')
now just access name directly
print friend.name # jeremy
There is a good amount to learn about python classes luckily python provides excellent documentation for whatever version you are on.
in this example, to create a new friend you need to instantiate it with a name.
What you are referring to is default keyword arguments. The way you have specified it in your example means that name is required in the constructor. The way to make it default (and be able to be set after the constructor) would look like this:
class Friend(object):
def __init__(self,name=''):
self.name = name
def set_name(self,name):
self.name=name
def get_name(self):
return self.name
Now your class can be instantiated without a name:
aFriend = Friend()
As suggested in comments, it is not "considered pythonic" to have setters and getters for a basic attribute. But, if that attribute requires computation, then you can make it a property:
class Friend(object):
def __init__(self, firstname='', lastname=''):
self.firstname = firstname
self.lastname = lastname
#property
def firstname(self):
return self._first
#firstname.setter
def firstname(self, n):
self._first = n.capitalize()
#property
def lastname(self):
return self._last
#lastname.setter
def lastname(self, n):
self._last = n.capitalize()
#property
def fullname(self):
return "{0} {1}".format(self.firstname, self.lastname)
f = Friend('frank')
f.lastname = 'smith'
f.firstname
# 'Frank'
f.lastname
#'Smith'
f.fullname
#'Frank Smith'

Categories

Resources