Related
I have 2 python classes one subclasses the other
class A:
def __init__(some params):
do something()
def method(params):
return A_new_A_instance
class B(A)
def __init__(some params):
super().__init__(some params)
def new_method(params):
a_instance=super.method(params)
return B(a)
The above works fine for some of the methods I'm using heavily.
The issue is that class A has a lot of methods some I'm using as is others I'm modifying etc. And a few I don't care about. Most of the methods in A returns another instance of A (like selecting, adding, re-ordering data) But I want to make sure that whichever A.method() I call I want return an instance of B when I do B.method().
Is there a magic way to do this for all methods of A or do I need to over them one by one?
As long as the constructor of both A and B are the same (they take the same parameters) you can use a factory function to create new instances of A and override it for B:
class A:
def __init__(self, *params):
pass
def _create_new_instance(self, *params):
return A(*params)
def method(self, *params):
# this will either call A._create_new_instance or
# B._create_new_instance depending on type(self)
return self._create_new_instance(*params)
class B(A):
def __init__(self, *params):
super().__init__(self, *params)
def _create_new_instance(self, *params):
return B(*params)
def new_method(self, *params):
new_b = self.method(*params)
do_something_new(new_b)
return new_b
assert isinstance(A().method(), A)
assert isinstance(B().method(), B)
I guess I did not word my question properly.
I was looking for a way to use the existing methods from the superclass in the subclass without knowing what they are (or not bothering to know).
The solution I came up with is follows:
have a function that queries available methods in the superclass
def get_methods(class_instance):
method_list = [attribute for attribute in dir(class_instance) if callable(getattr(class_instance, attribute))
and attribute.startswith('__') is False]
return
then you can put something like this in the __init__ to get those methods into the subclass. I'm avoiding the __ methods, since I do not care about most of them and also want to set some of them myself later on like __str__ or __add__.
methods=get_methods(super())
for method_name in methods:
method = getattr(super(), method_name)
self.__setattr__(method_name, classmethod(method))
I am using python and have an object, that object has a method. I am looking for a simple way, to replace the entire object from within that function.
E.g
class a():
def b(self):
self = other_object
How can you do that?
Thanks
You use a proxy/facade object to hold a reference to the actual object, the self if you wish and that proxy (better term than Facade, but not changing my code now) is what the rest of your codebase sees. However, any attribute/method access is forwarded on to the actual object, which is swappable.
Code below should give you a rough idea. Note that you need to be careful about recursion around __the_instance, which is why I am assigning to __dict__ directly. Bit messy, since it's been a while I've written code that wraps getattr and setattr entirely.
class Facade:
def __init__(self, instance):
self.set_obj(instance)
def set_obj(self, instance):
self.__dict__["__theinstance"] = instance
def __getattr__(self, attrname):
if attrname == "__theinstance":
return self.__dict__["__theinstance"]
return getattr(self.__dict__["__theinstance"], attrname)
def __setattr__(self, attrname, value):
if attrname == "__theinstance":
self.set_obj(value)
return setattr(self.__dict__["__theinstance"], attrname, value)
class Test:
def __init__(self, name, cntr):
self.name = name
self.cntr = cntr
def __repr__(self):
return "%s[%s]" % (self.__class__.__name__, self.__dict__)
obj1 = Test("first object", 1)
obj2 = Test("second", 2)
obj2.message = "greetings"
def pretend_client_code(facade):
print(id(facade), facade.name, facade.cntr, getattr(facade, "value", None))
facade = Facade(obj1)
pretend_client_code(facade)
facade.set_obj(obj2)
pretend_client_code(facade)
facade.value = 3
pretend_client_code(facade)
facade.set_obj(obj1)
pretend_client_code(facade)
output:
4467187104 first object 1 None
4467187104 second 2 None
4467187104 second 2 3
4467187104 first object 1 None
So basically, the "client code" always sees the same facade object, but what it is actually accessing depends on what your equivalent of def b is has done.
Facade has a specific meaning in Design Patterns terminology and it may not be really applicable here, but close enough. Maybe Proxy would have been better.
Note that if you want to change the class on the same object, that is a different thing, done through assigning self.__class__ . For example, say an RPG game with an EnemyClass who gets swapped to DeadEnemyClass once killed: self.__class__ = DeadEnemyClass
You can't directly do that. What you can do is save it as an instance variable.
class A():
def __init__(self, instance=None):
self.instance = val or self
# yes, you can make it a property as well.
def set_val(self, obj):
self.instance = obj
def get_val(self):
return self.instance
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.
Original source: Is it safe to replace a self object by another object of the same type in a method?
I have a class factory method that is used to instantiate an object. With multiple objects are created through this method, I want to be able to compare the classes of the objects. When using isinstance, the comparison is False, as can be seen in the simple example below. Also running id(a.__class__) and id(b.__class__), gives different ids.
Is there a simple way of achieving this? I know that this does not exactly conform to duck-typing, however this is the easiest solution for the program I am writing.
def factory():
class MyClass(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
return MyClass()
a = factory()
b = factory()
print(a.compare(b))
The reason is that MyClass is created dynamically every time you run factory. If you print(id(MyClass)) inside factory you get different results:
>>> a = factory()
140465711359728
>>> b = factory()
140465712488632
This is because they are actually different classes, dynamically created and locally scoped at the time of the call.
One way to fix this is to return (or yield) multiple instances:
>>> def factory(n):
class MyClass(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
for i in range(n):
yield MyClass()
>>> a, b = factory(2)
>>> a.compare(b)
Comparison Result: True
is a possible implementation.
EDIT: If the instances are created dynamically, then the above solution is invalid. One way to do it is to create a superclass outside, then inside the factory function subclass from that superclass:
>>> class MyClass(object):
pass
>>> def factory():
class SubClass(MyClass):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
return SubClass()
However, this does not work because they are still different classes. So you need to change your comparison method to check against the first superclass:
isinstance(other, self.__class__.__mro__[1])
If your class definition is inside the factory function, than each instance of the class you create will be an instance of a separate class. That's because the class definition is a statement, that's executed just like any other assignment. The name and contents of the different classes will be the same, but their identities will be distinct.
I don't think there's any simple way to get around that without changing the structure of your code in some way. You've said that your actual factory function is a method of a class, which suggests that you might be able to move the class definition somewhere else so that it can be shared by multiple calls to the factory method. Depending on what information you expect the inner class to use from the outer class, you might define it at class level (so there'd be only one class definition used everywhere), or you could define it in another method, like __init__ (which would create a new inner class for every instance of the outer class).
Here's what that last approach might look like:
class Outer(object):
def __init__(self):
class Inner(object):
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
self.Inner = Inner
def factory(self):
return self.Inner()
f = Outer()
a = f.factory()
b = f.factory()
print(a.compare(b)) # True
g = Outer() # create another instance of the outer class
c = g.factory()
print(a.compare(c)) # False
It's not entirely clear what you're asking. It seems to me you want a simpler version of the code you already posted. If that's incorrect, this answer is not relevant.
You can create classes dynamically by explicitly constructing a new instance of the type type.
def compare(self, other):
...
def factory():
return type("MyClass", (object,), { 'compare': compare }()
type takes three arguments: the name, the parents, and the predefined slots. So this will behave the same way as your previous code.
Working off the answer from #rassar, and adding some more detail to represent the actual implementation (e.g. the factory-method existing in a parent class), I have come up with a working example below.
From #rassar's answer, I realised that the class is dynamically created each time, and so defining it within the parent object (or even above that), means that it will be the same class definition each time it is called.
class Parent(object):
class MyClass(object):
def __init__(self, parent):
self.parent = parent
def compare(self, other):
print('Comparison Result: {}'.format(isinstance(other, self.__class__)))
def factory(self):
return self.MyClass(self)
a = Parent()
b = a.factory()
c = a.factory()
b.compare(c)
print(id(b.__class__))
print(id(c.__class__))
I am trying to create a subclass which acts as a list of custom classes. However, I want the list to inherit the methods and attributes of the parent class and return a sum of the quantities of each item. I am attempting to do this using the __getattribute__ method, but I cannot figure out how to pass arguments to callable attributes. The highly simplified code below should explain more clearly.
class Product:
def __init__(self,price,quantity):
self.price=price
self.quantity=quantity
def get_total_price(self,tax_rate):
return self.price*self.quantity*(1+tax_rate)
class Package(Product,list):
def __init__(self,*args):
list.__init__(self,args)
def __getattribute__(self,*args):
name = args[0]
# the only argument passed is the name...
if name in dir(self[0]):
tot = 0
for product in self:
tot += getattr(product,name)#(need some way to pass the argument)
return sum
else:
list.__getattribute__(self,*args)
p1 = Product(2,4)
p2 = Product(1,6)
print p1.get_total_price(0.1) # returns 8.8
print p2.get_total_price(0.1) # returns 6.6
pkg = Package(p1,p2)
print pkg.get_total_price(0.1) #desired output is 15.4.
In reality I have many methods of the parent class which must be callable. I realize that I could manually override each one for the list-like subclass, but I would like to avoid that since more methods may be added to the parent class in the future and I would like a dynamic system. Any advice or suggestions is appreciated. Thanks!
This code is awful and really not Pythonic at all. There's no way for you to pass extra argument in the __getattribute__, so you shouldn't try to do any implicit magic like this. It would be better written like this:
class Product(object):
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def get_total_price(self, tax_rate):
return self.price * self.quantity * (1 + tax_rate)
class Package(object):
def __init__(self, *products):
self.products = products
def get_total_price(self, tax_rate):
return sum(P.get_total_price(tax_rate) for P in self.products)
If you need, you can make the wrapper more generic, like
class Package(object):
def __init__(self, *products):
self.products = products
def sum_with(self, method, *args):
return sum(getattr(P, method)(*args) for P in self.products)
def get_total_price(self, tax_rate):
return self.sum_with('get_total_price', tax_rate)
def another_method(self, foo, bar):
return self.sum_with('another_method', foo, bar)
# or just use sum_with directly
Explicit is better than implicit. Also composition is usually better than inheritance.
You have a few points of confusion here:
1) __getattribute__ intercepts all attribute access, which isn't what you want. You only want your code to step in if a real attribute doesn't exist, so you want __getattr__.
2) Your __getattribute__ is calling the method on the list elements, but it shouldn't be doing real work, it should only return a callable thing. Remember, in Python, x.m(a) is really two steps: first, get x.m, then call that thing with an argument of a. Your function should only be doing the first step, not both steps.
3) I'm surprised that all the methods you need to override should be summed. Are there really that many methods, that really all should be summed, to make this worthwhile?
This code works to do what you want, but you might want to consider more explicit approaches, as others suggest:
class Product:
def __init__(self,price,quantity):
self.price = price
self.quantity = quantity
def get_total_price(self,tax_rate):
return self.price*self.quantity*(1+tax_rate)
class Package(list):
def __init__(self,*args):
list.__init__(self,args)
def __getattr__(self,name):
if hasattr(self[0], name):
def fn(*args):
tot = 0
for product in self:
tot += getattr(product,name)(*args)
return tot
return fn
else:
raise AttributeError
Things to note in this code: I've made Package not derive from Product, because all of its Product-ness it gets from delegation to the elements of the list. Don't use in dir() to decide if a thing has an attribute, use hasattr.
To answer your immediate question, you call a function or method retrieved using getattr() the same way you call any function: by putting the arguments, if any, in parentheses following the reference to the function. The fact that the reference to the function comes from getattr() rather than an attribute access doesn't make any difference.
func = getattr(product, name)
result = func(arg)
These can be combined and the temporary variable func eliminated:
getattr(product, name)(arg)
In addition to what Cat Plus Plus said, if you really want to invoke magic anyway (please don't! There are unbelievably many disturbing surprises awaiting you with such an approach in practice), you could test for the presence of the attribute in the Product class, and create a sum_with wrapper dynamically:
def __getattribute__(self, attr):
return (
lambda *args: self.sum_with(attr, *args)
if hasattr(Product, attr)
else super(Package, self).__getattribute__(attr)
)
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>