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.
Related
How can I get slots to work with #property for the class below. I have several thousand instances of below class which is causing memory issues and so I added the slots
I created instances with data and then add location information later to the instances.
After adding slots my instance creation is not working and I am getting the following error
AttributeError: 'Host' object has no attribute '_location'
class Host(object):
__slots__ = ['data', 'location']
def __init__(self, data, location=''):
self.data = data
self.location = location
#property
def location(self):
return self._location
#location.setter
def location(self, value):
self._location = value.lower()
def __repr__(self):
if self.location == '':
self.loc = 'Not Found'
else:
self.loc = self.location
return 'Host(name={}, location={})'.format(self.name, self.loc)
__slots__ works by creating descriptors on the class that have direct access to the in-memory data structure of your instance. You are masking the location descriptor with your property object, and you defined a new attribute _location than is not in the slots.
Make _location the slot (as that is the attribute you are actually storing):
class Host(object):
__slots__ = ['data', '_location']
The location property (also a descriptor object) can then properly assign to self._location, an attribute backed by the slot descriptor.
Note that you do not need to use self.loc in the __repr__, just make that a local variable instead. You also are trying to use a self.name attribute which doesn't exist; it is not clear what value that is supposed to be however:
def __repr__(self):
loc = self.location or 'Not Found'
name = self.data['name'] # or some other expression
return 'Host(name={}, location={})'.format(name, loc)
The definition for __slots__ should have the names of the underlying attributes that will store the data referenced by your properties. In the example below, name mangling is invoked for variables that should not be accessed outside of the class. The code is similar to yours and has no errors according to the PEP8 online website.
#! /usr/bin/env python3
def main():
print(Host('Hello, world!', 'Earth'))
print(Host('Hello, Xyz!'))
class Host:
__slots__ = '__name', '__location'
def __init__(self, name, location=''):
self.name = name
self.location = location
def __repr__(self):
return '{!s}({!r}, {!r})'.format(
type(self).__name__,
self.name,
self.location
)
#property
def name(self):
return self.__name
#name.setter
def name(self, value):
self.__name = value
#property
def location(self):
return self.__location
#location.setter
def location(self, value):
self.__location = value.casefold() if value else 'Not Found'
if __name__ == '__main__':
main()
I would like to know if there is an easy way to do some identical edits on several methods of a class. An example :
class Dog():
def __init__(self):
self.name = 'abc'
self.age = 1
def setName(self, newValue):
self.name = newValue
def setAge(self, newValue):
self.age = newValue
class TalkingDog(Dog):
def __init__(self):
super().__init__()
# The end is in pseudo code :
for method in TalkingDog.allMethods :
method = method + 'print('I have been edited !')'
I know that I can also overwrite each method but in a situation with tens of methods, that will be a little boring...
So I tried this :
class TalkingDog(Dog):
def __init__(self):
super().__init__()
for method in self.__dir__():
if method.startswith('set'):
oldMethod = getattr(self, method)
def _newMethod(newValue):
oldMethod(newValue)
print('I have been edited !')
setattr(self, method, _newMethod)
a = TalkingDog()
print(a.setName) >>> <function TalkingDog.__init__.<locals>._newMethod at 0x0000000002C350D0>
That almost works but setName is not anymore a method. It's an attribute which contains a function. I completely understand why but I'm trying to get a cleaner result. With that result, I risk of having problems later. For example I can't use the library pickle with that object (got the error _pickle.PicklingError: Can't pickle <function TalkingDog.__init__.<locals>._newMethod at 0x00000000003DCBF8>: attribute lookup _newMethod on __main__ failed).
The Pythonic way to do this is probably to use the descriptor protocol, which is also what properties use:
class VocalAttribute:
def __init__(self, name, feedback):
"""Called when you first create the descriptor."""
self.name = name # the name of the attribute 'behind' the property
self.feedback = feedback # the feedback to show when the value changes
def __get__(self, obj):
"""Called when you get the descriptor value."""
return getattr(obj, self.name)
def __set__(self, obj, value):
"""Called when you set the descriptor value."""
prev = getattr(obj, self.name, None)
if value != prev:
setattr(obj, self.name, value)
print(self.feedback)
def __delete__(self, obj):
"""Called when you delete the descriptor value."""
delattr(obj, self.name)
class Foo:
bar = VocalAttribute('_bar', 'I have been edited!')
foo = Foo()
print('1.')
foo.bar = 'hello'
print('2.')
foo.bar = 'hello'
print('3.')
foo.bar = 'world'
Output:
1.
I have been edited!
2.
3.
I have been edited!
Note that this only gives feedback when the new value is different to the old one - you can tweak the behaviour as needed in __set__. It also means you can directly read from and assign to foo.bar, rather than needing to call getters and setters (what is this, Java?)
since decorator could explicit called here a way to use it:
def updater(obj, call_back, call_back_args=(), call_back_kw=None, replace=False):
# ability to be called on the fly with different args and kw for the callback
# now it returns the updated obj (instance or class)
# but could a be factory returning a new obj in this case make a copy of obj, update this coy and return it
def update_function(fn, *args, **kw):
def wrapper(*args, **kw):
if replace:
# call only the callback
res = call_back(*call_back_args, **call_back_kw)
else:
res = fn(*args, **kw)
call_back(*call_back_args, **call_back_kw)
return res
return wrapper
# get all methods of the obj
# and apply update_function (a decorator) to all methods
for name, m in inspect.getmembers(
obj, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x)):
# make the selection here
# could be made on the name for instance
if not name.startswith('_'):
new_m = update_function(m)
setattr(obj, name, new_m)
return obj
# declare a callback
def call_back(*args, **kw):
# simple callback
print("I have been edited and called with %r args and %r kw " % (args, kw))
a = Dog()
# could be called on instance or class
# apply the callback on all "public" methods
updater(
a,
call_back,
call_back_args=(2, 3, 4),
call_back_kw={"kw1": "v_1"}
)
I am trying Overloading an operator forcing it to return an object of the same instance of the current class not the parent class where the method was overloaded.
class Book:
def __init__(self,name,pages):
self.name=name
self.pages=pages
def __add__(self,other):
return Book(self.name,(self.pages + other.pages))
class Encyclopedia(Book):
def __init__(self,name,pages):
Book.__init__(self,name,pages)
a=Encyclopedia('Omina',234)
b=Encyclopedia('Omnia2',244)
ab=a+b
print ab
Out: <__main__.Book instance at 0x1046dfd88>
For instance in this case I would like to return an Encycolpedia instance (not a Book instance) without overloading another time the operator __add__ with the same line with Encyclopedia instead of Book I have tried:
return self(self.name,(self.pages + other.pages))
But it doesn't work.
What if the Class Enclcopedia has another attribute:
class Encyclopedia(Book):
def __init__(self,name,pages,color):
Book.__init__(self,name,pages)
self.color=color
You could utilize self.__class__ instead of casting to Book. Your original add function should look like:
def __add__(self,other):
return self.__class__(self.name,(self.pages + other.pages))
You would need to do it something like this, which overloads the base class's methods (in this case, generally by calling them first and then doing additional processing on the result — although that's not a requirement):
class Book(object):
def __init__(self, name, pages):
self.name = name
self.pages = pages
def __add__(self, other):
return Book(self.name, self.pages+other.pages)
def __str__(self):
classname = self.__class__.__name__
return '{}({}, {})'.format(classname, self.name, self.pages)
class Encyclopedia(Book):
def __init__(self, name, pages, color):
Book.__init__(self, name, pages)
self.color = color
def __add__(self, other):
tmp = super(Encyclopedia, self).__add__(other)
return Encyclopedia(tmp.name, tmp.pages, self.color+other.color)
def __str__(self):
classname = self.__class__.__name__
return '{}({!r}, {}, {!r})'.format(classname, self.name, self.pages,
self.color)
a = Encyclopedia('Omina', 234, 'grey')
b = Encyclopedia('Omnia2', 244, 'blue')
ab = a+b
print(ab) # -> Encyclopedia('Omina', 478, 'greyblue')
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)
I was recently writing a definition for a pretty basic data class in Python and I came up with the following:
class A:
def __init__(self, **kwargs):
self.__a1 = kwargs.get('some_value', -1)
#property
def a1(self):
return self.__a1
#a1.setter
def a1(self, new_a1):
self.__a1 = new_a1
And it goes on. In this case, the value -1 could be replaced with a variety of "null" values: -1, "", [], etc., and some_value comes from an Enum I defined earlier.
Because the class definition contains several of these property definitions, and they're all very "same-y", I'd like to write a function to do this for me. I'm pretty sure it's possible in Python but I've never tried it so I was hoping for some pointers.
Assuming you want to simplify the repetitive property definitions, you can use a generic descriptor to simplify this significantly:
class ProtectedAttribute(object):
"""Basic descriptor functionality for a protected attribute.
Args:
name (str): The name of the attribute to back the descriptor
(usually the name the descriptor is assigned to with a single
additional leading underscore).
"""
def __init__(self, name, **kwargs):
self.name = name
def __get__(self, obj, typ):
return getattr(obj, self.name)
def __set__(self, obj, value):
setattr(obj, self.name, value)
def __delete__(self, obj):
delattr(obj, self.name)
Now you can just do:
class A(object):
a1 = ProtectedAttribute('__a1')
def __init__(self, **kwargs):
self.a1 = kwargs.get("some_value", -1)
Note also the use of dict.get to simplify __init__.