How to override a method of a member object? - python

In python 3.4 I have a member object through composition.
I would like to override one of it's member functions.
def class Foo:
def __init__(self, value):
self.value = value
def member_obj.baz(baz_self, arg):
print("my new actions on {}".format(arg))
Foo.member_obj.baz(arg) #the original function
foo_inst = Foo(2)
bar = Bar(*bar_parameters) #from a third party module
setattr(foo_inst, "member_obj", bar) #it did not "stick" when I did foo_inst.member_obj = bar
foo_inst.member_obj.baz("some argument")
It does not make sense to inherit from the Bar class.
I also only want this different behaviour to occur if the object is inside Foo. I use Bar in many other places and would like to retain the same way of calling the method. I.e. I would like to avoid wrapping it in Foo.baz.
Is it even possible to do something like the def member_obj.baz and is it a good idea?
It would be similar to this: https://softwareengineering.stackexchange.com/questions/150973/what-are-the-alternatives-to-overriding-a-method-when-using-composition-instea

Are you trying to do something like this?
class B():
def __init__(self):
self.x = None
def fun(self):
print("Assigning value to attribute of object of class B.\n")
self.x = "Value of B object's attribute"
class A():
def __init__(self):
self.value = B()
def fun(self):
print("Screw this, I'll do something else this time!\n")
self.value.x = 13
def override(self):
# Edit: you can assign any identifier (that is not reserved) to
# any type of object or method AND the "fun" ("really self.fun")
# above is visible from here, since we passed "self" as an
# argument
self.value.fun = self.fun
myObj = B()
myOtherObj = A()
myOtherObj.override()
myObj.fun()
myOtherObj.value.fun()

Related

Python - Class object as function argument: Object only - Class not in argument

I am trying to write a function taking a string as an argument and using this argument as a class object.
Note that my explanantion might be strangely formulated sice I could not find an answer online. The MWE below should clarify what I mean, the problematic line is indicated.
Edit: in the MWE, "print" is an example. I need to be able to call the object to update it, print it or, in the case of a list, append to it. I need access to the object itself, not the value of the object.
MWE
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Define function
def myFunction (parameter):
print(myObject.parameter)##### This line is currently not possible.
# Use class
myObject = myClass()
# Use function
myFunction("one")
I am not trying to append a new object to the class, only to call an existing object.
Is this even possible?
Looks like you need the built-in function called getattr
my_object = myClass()
def my_function(parameter):
print(getattr(my_object, parameter, None))
also this is not the best practice to call objects from outer scope like that. i'd suggest to use dict magic methods:
class MyClass:
def __init__(self):
self.one = "Test"
self.two = "Plop"
def __getitem__(self, parameter):
return getattr(self, parameter, None)
def __setitem__(self, parameter, value):
return setattr(self, parameter, value)
my_obj = MyClass()
parameter = "x"
print(my_obj[parameter])
my_obj[parameter] = "test"
print(my_obj.x)
You need to use getarttr():
# Create a class
class myClass():
def __init__(self):
self.one = "Test"
self.two = "Plop"
# Use class
myObject = myClass()
# Define function
def myFunction(parameter):
print(getattr(myObject, parameter))##### This line is currently possible.
# Use function
myFunction("one")

Dynamically adopt the methods of an instance of another class

I have a case, where I have an instance of a class in python which holds instances of other classes. For my use case, I would like a way to use the methods of the "inner" classes from the outer class without referencing the attribute holding the inner class.
I have made a simplistic example here:
class A:
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_goodbye(self):
print("Goodbye")
class C:
def __init__(self, other_instance):
self.other_instance= other_instance
def say_good_night(self):
print("Good night")
my_a = A()
my_c = C(other_instance=my_a)
# How to make this possible:
my_c.say_hi()
# Instead of
my_c.other_instance.say_hi()
Class inheritance is not possible, as the object passed to C may be an instance of a range of classes. Is this possible in Python?
I think this is the simplest solution although it is possible with metaprogramming.
class A:
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_goodbye(self):
print("Goodbye")
class C:
def __init__(self, other_class):
self.other_class = other_class
C._add_methods(other_class)
def say_good_night(self):
print("Good night")
#classmethod
def _add_methods(cls, obj):
type_ = type(obj)
for k, v in type_.__dict__.items():
if not k.startswith('__'):
setattr(cls, k, v)
my_a = A()
my_c = C(other_class=my_a)
my_c.say_hi()
output :
Hi
First we get the type of passed instance, then we iterate through it's attribute (because methods are attributes of the class not the instance).
If self.other_class is only needed for this purpose, you can omit it as well.
So, because you have done:
my_a = A() and my_c = C(other_class=my_a).
my_c.other_class is the same as my_a asthey point to the same location in memory.
Therefore, as you can do my_a.say_hi() you could also do my_c.other_class.say_hi().
Also, just a note, as you are calling A() before you store it into other_classes, I would probably rename the variable other_classes to class_instances.
Personally, I think that would make more sense, as each of those classes would have already been instantiated.

Add attribute to python class

Consider the following code:
class Foo():
pass
Foo.entries = dict()
a = Foo()
a.entries['1'] = 1
b = Foo()
b.entries['3'] = 3
print(a.entries)
This will print:
{'1': 1, '3': 3}
because the entries is added as static attribute. Is there a way monkey patch the class definition in order to add new attributes (without using inheritance).
I managed to find the following way but it looks convoluted to me:
def patch_me(target, field, value):
def func(self):
if not hasattr(self, '__' + field):
setattr(self, '__' + field, value())
return getattr(self, '__' + field)
setattr(target, field, property(func))
patch_me(Foo, 'entries', dict)
Ordinarily, attributes are added either by the __init__() function or after instantiating:
foo = Foo()
foo.bar = 'something' # note case
If you want to do this automatically, inheritance is by far the simplest way to do so:
class Baz(Foo):
def __init__(self):
super().__init__() # super() needs arguments in 2.x
self.bar = 'something'
Note that classes don't need to appear at the top level of a Python module. You can declare a class inside a function:
def make_baz(value):
class Baz(Foo):
def __init__(self):
super().__init__() # super() needs arguments in 2.x
self.bar = value()
return Baz()
This example will create a new class every time make_baz() is called. That may or may not be what you want. It would probably be simpler to just do this:
def make_foo(value):
result = Foo()
result.bar = value()
return result
If you're really set on monkey-patching the original class, the example code you provided is more or less the simplest way of doing it. You might consider using decorator syntax for property(), but that's a minor change. I should also note that it will not invoke double-underscore name mangling, which is probably a good thing because it means you cannot conflict with any names used by the class itself.

Declaring class object datatypes

I have a class Foo which contains a datamember of type Bar. I can't make a generalized, "default" Bar.__init__() - the Bar object is passed into the Foo.__init__() method.
How do I tell Python that I want a datamember of this type?
class Foo:
# These are the other things I've tried, with their errors
myBar # NameError: name 'myBar' is not defined
Bar myBar # Java style: this is invalid Python syntax.
myBar = None #Assign "None", assign the real value in __init__. Doesn't work
#####
myBar = Bar(0,0,0) # Pass in "default" values.
def __init__(self, theBar):
self.myBar = theBar
def getBar(self):
return self.myBar
This works, when I pass in the "default" values as shown. However, when I call getBar, I do not get back the one I passed in in the Foo.__init__() function - I get the "default" values.
b = Bar(1,2,3)
f = Foo(b)
print f.getBar().a, f.getBar().b, f.getBar().c
This spits out 0 0 0, not 1 2 3, like I'm expecting.
If I don't bother declaring the myBar variable, I get errors in the getBar(self): method (Foo instance has no attribute 'myBar').
What's the correct way to use a custom datamember in my object?
You don't need to tell Python you are going to add a certain data member – just add it. Python is more dynamic than e.g. Java in this regard.
If bar instances are essentially immutable (meaning they are not changed in practice), you can give the default instance as default value of the __init__() parameter:
class Foo:
def __init__(self, the_bar=Bar(0,0,0)):
self.my_bar = the_bar
All Foo instances uisng the default value will share a single Bar instance. If the Bar instance might be changed, this is probably not what you want, and you should use this idiom in this case:
class Foo:
def __init__(self, the_bar=None):
if the_bar is None:
the_bar = Bar(0,0,0)
self.my_bar = the_bar
Note that you shouldn't usually write getters and setters in Python. They are just unnecessary boilerplate code slowing down your application. Since Python supports properties, you also don't need them to be future-proof.
The correct way is to do nothing other than assign it in the constructor.
class Foo:
def __init__(self, bar):
self.bar = bar
def getbar(self):
return self.bar
You definitely don't have to declare bar ahead of time.
It sounds like you want Foo.bar to default to a value if one isn't specified so you might do something like this:
class Foo:
def __init__(self, bar=None):
# one of many ways to construct a new
# default Bar if one isn't provided:
self._bar = bar if bar else Bar(...)
#property
def bar(self):
"""
This isn't necessary but you can provide proper getters and setters
if you prefer.
"""
return self._bar
#bar.setter
def bar(self, newbar):
"""
Example of defining a setter
"""
return self._bar = newbar
Typically just naming the variable appropriately and omitting the setter is considered more more 'pythonic'.
class Foo:
def __init__(self, bar=None):
self.bar = bar if bar else Bar(...)
You don't declare variables in Python, and variables are untyped.
Just do:
class Foo(object):
def __init__(self, bar):
self.bar = bar
def getbar(self):
return self.bar
I suspect that the issue is caused by you using old-style classes, which are kind of odd. If you inherit from object, you get a new-style class, which is designed to be much less surprising.

Is it safe to replace a self object by another object of the same type in a method?

I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>

Categories

Resources