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

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)

Related

Python, delete attribute from instance but not from source

I have the following three classes measurement, submeasurement and environment. submeasurement takes an instance of measurement and environment takes an instance of submeasurement as input argument.
I would like to keep the attributes of each class in order. Therefore I wrote the method set_cls_as_attr, which is supposed to convert: self.submeasurement.measurement --> self.measurement and to delete: self.submeasurement.measurement.
class environment():
def __init__(self, submeasurement):
self.submeasurement = submeasurement
self.set_cls_as_attr(submeasurement, 'measurement')
def set_cls_as_attr(self, obj, cls_name):
setattr(self, cls_name, getattr(obj, cls_name))
print(obj.__class__.__name__ + '.' + cls_name)
if hasattr(self, obj.__class__.__name__ ):
if hasattr(eval('self.' + obj.__class__.__name__), cls_name):
delattr(eval('self.' + obj.__class__.__name__), cls_name)
class measurement():
def __init__(self):
self.name = self.__class__.__name__
class submeasurement():
def __init__(self, measurement):
self.name = self.__class__.__name__
self.measurement = measurement
meas=measurement()
sub_meas=submeasurement(meas)
env= environment(sub_meas)
This works fine, but it also deletes sub_meas.measurement and this I do not want to happen. Is there a way to do that? I also tried __dict__.pop('measurement') but this does the same.

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

Python class objectivity convention

Is it a good style to create classes like that ? I read the PEP8 document but I didn't saw any good example. If not how is it a proper way ? Thanks for any answers.
class Zone:
def __init__(self, index=None, name=None):
self._index = index
self._name = name
#property
def index(self):
return self._index
#property
def name(self):
return self._name
#index.setter
def index(self, index):
self._index = index
#name.setter
def name(self, name):
self._name = name
Your setters and getters don't do anything. With your implementation, the user of this class does this:
z = Zone()
z.name = 'foo'
print(z.name)
Compare to this implementation:
class Zone:
def __init__(self, index=None, name=None):
self.index = index
self.name = name
z = Zone()
z.name = 'foo'
print(z.name)
It works exactly the same with a lot less code.
Unless you do anything in your setters and/or getters, you don't need them.
If what you intend doing is encapsulating your data and setting it with setters and getting it with getters, then what you did will not be helpful. you declared the _name and _index as protected, it does not mean it cannot be accessed by extenal functions, so functions outside the class can easily access and change them, making your getter and setter to be useless.
However, you can declare them as private by using one additional underscore in front, so that your property class will be removed and then the setters class will be useful, it will no longer be accessed by external functions.
class Zone:
def __init__(self,index=None,name=None):
self.__index = index
self.__name = name
def index(self, index):
self.__index = index
def name(self, name):
self.__name = name
def get_name(self):
return self.__name
zone=Zone()
zone.name('ben')
print(zone.get_name())
>>>ben
print(zone.__name)
>>> AttributeError: 'Zone' object has no attribute '__name'

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

Mutually Reference-able Instances in Python

Say I have a pair of instances that reference one another mutually. Is there a preferable manner to structure this relationship than the following.
class Human():
def __init__(self, name):
self.name = name
self.pet = Dog('Sparky', self)
def pet(self, animal):
self.pet.receive_petting()
class Dog(Pet):
def __init__(self, name, owner):
self.name = name
self.owner = owner
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
The thing I don't like is that the relationship needs to be specified in two places. Any ideas on how to make this dryer?
I would break this into three classes:
class Human():
def __init__(self, name):
self.name = name
class Dog(Pet):
def __init__(self, name):
self.name = name
def bark_at(self, person):
"do something"
class OwnerPetRelation():
def __init__(self, dog, human):
self.owner=human
self.pet=dog
Now, one owner can also have many dogs, we just need to define as many OwnerPetRelations.
Similarly, a dog can also belong to multiple owners now.
I would create a method on Human that allows you to add pets (since a human might have many pets):
class Human():
def __init__(self, name):
self.name = name
self.pets = []
def add_pet(self, pet):
pet.owner = self
self.pets.append(pet)
def pet(self, animal):
for pet in self.pets:
pet.receive_petting()
class Dog(Pet):
def __init__(self, name):
self.name = name
self.owner = None
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
This can be used as follows
human = Human('Jim')
human.add_pet(Dog('Woof'))
This approach can of course also be used for just a single pet and one could also extend it to allow pets to be owned by many humans.
There's nothing really Python-specific here; this is just a limitation of constructor-based dependency injection. It's hard to inject a reference to another object that cannot have been created yet. Instead, you can create an object that has a reference to something that will have a reference to the other object. For instance, you can pass a function to the constructor that will be able to return the value:
class Human():
def __init__(self,name,dog):
self.name = name
self._dog = dog
#property
def dog(self):
return self._dog()
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
return self._human()
Then you can use it like this:
human = None
dog = Dog('fido',lambda: human)
human = Human('john',lambda: dog)
print(dog.human.name)
print(human.dog.name)
john
fido
It is not hard to update this so that the property function caches the value, of course. E.g.:
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
try:
return self._human_
except AttributeError:
self._human_ = self._human()
return self._human_

Categories

Resources