Python class inheritance - Base is modified by a subclass - python

Let's say I have a class called Test with an attribute items. Then I create a subclass called Best. Which has a method that modifies the classes attribute items. But it even modifies Test's items and I what it to modify items only for Best.
class Test():
items = []
class Best(Test):
def method(self):
type(self).items.append("a test")
>>> Best().method()
>>> Best.items
["a test"]
>>> Test.items
["a test"] # This is what I don't want.

You have declared items as an attribute of the superclass itself, so all instances of Test and it's subclasses will share the same list. Instead declare it in Test's __ init __ method, so there is one list per instance.
In Best, just append to self.items, and only the Best instance's list will be updated.
class Test(object):
def __ init __(self)
self.items = []
class Best(Test): # Best must inherit from Test
def method(self):
self.items.append("a test")

In Python you can get what you are asking by using "private" members...
class Base(object):
def __init__(self):
self.__mine = 42 # note the double underscore
def baseMethod(self):
return self.__mine
class Derived(Base):
def __init__(self):
Base.__init__(self)
self.__mine = 99
def derivedMethod(self):
return self.__mine
obj = Derived()
print(obj.baseMethod(), obj.derivedMethod()) ## ==> 42, 99
this works because at compile time Python will replace the name __mine with _Base__mine when compiling Base and with _Derived__mine when compiling Derived.
Note however that in Python while this is possible in my experience it's not used very often. Deriving a class in many cases is just not needed thanks to "duck typing" and to delegation, something that is not possible in languages like C++ or Java.

The only possible way to do this is to create a new items on the subclass -- where else is this new list meant to come from? Also type(self) is redundant. The lookup machinery looks up attributes on the class if it cannot find the attribute on the instance. Better yet, if you don't need an instance then declare the method to be a class method.
eg.
class Test:
items = []
#classmethod
def method_test(cls):
cls.items.append('test')
class Best(Test):
items = []
#classmethod
def method_best(cls):
cls.items.append('best')
Test.method_test()
assert Test.items == ['test']
assert Best.items == []
Test.items = []
Best.method_test()
Best.method_best()
assert Test.items == []
assert Best.items == ['test', 'best']
Note that method_test works on the Best class when called from the Best class.

Your Best class is modifying Test (which I assume it's supposed to be inheriting from) because Best doesn't have its own items list. When you access Best.items, you're accessing the list where it is inherited from (i.e. from Test class). If you want a different list, you need to create it explicitly in the subclass Best:
class Best(Test):
items = [] # hide the inherited list with our own list
# ...

Your code doesn't do what you describe.
For one thing, Best is not a subclass of Test.
For another Best.method() produces
NameError: name 'self' is not defined
items is a Test class attribute.
t = Test()
t.items.append(1)
changes Test.items.
As defined B.items gives an AttributeError.
Even if I change:
class Best():
def method(self):
...
Best.method() does not run; method is an instance method. I need to use Best().method(). But then I get the items AttributeError.
class Best(Test):
def method(self):
...
does what you desribe. Best().method() modifies the Test.items - because the Test class attribute is shared with the subclass.
As shown in other answers, simply defining items for Best decouples its value from the Test class attribute
class Best(Test):
items = ['other']
...

Related

python: instaintiate all subclasses in a module along with their function output

i have a python script that contains the following classes and subclasses,
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def process(self):
pass
class B(A):
result= []
def process(self):
...code...
return result
class C(A):
result= []
def process(self):
...code...
return result
class D(A):
result= []
def process(self):
...code...
return result
so, each subclass of the abstract class A, does some process and appends the output to the 'result' list and then the 'process' function returns the result. I would like to know if there is a way to instantiate all subclasses and the function in these classes to be able to have access to return the results from the function for all subclasses in a list of lists.
I have looked at some answers in this post Python Instantiate All Classes Within a Module, but using the answers there I only get to substantiate the subclasses and do not know how to substantiate the 'process' function.
The hard part was to find all the subclasses, because it uses the less known __subclasses__ special method. From that it is straightforward: instanciate an object for each subclass and call the process method on it:
final_result = [] # prepare an empty list
for cls in A.__subclasses__(): # loop over the subclasses
x = cls() # instanciate an object
final_result.append(x.process()) # and append the result of the process method
But the Pythonic way would be to use a one line comprehension
final_result = [cls().process() for cls in A.__subclasses__()]

Classes returned from class factory have different IDs

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__))

Python Instantiate Class Within Class Definition

I am attempting to add a variable to a class that holds instances to the class. The following is a shortened version of my code.
class Classy :
def __init__(self) :
self.hi = "HI!"
# "CLASSIES" variable holds instances of class "Classy"
CLASSIES = []
for i in xrange(0,4) :
CLASSIES.append(Classy())
Upon running the code, I get the following error.
Traceback (most recent call last):
File "classy.py", line 6, in Classy
CLASSIES.append(Classy())
NameError: name 'Classy' is not defined
Is there another way to add instances of a class to a class/static variable within that class?
The class body is executed before the class is created. Therefore, you are attempting the instantiate the class before it exists. You can still attach instances to the class, but you have to create them after the class body finished, e.g.:
class Classy(object):
def __init__(self):
self.hi = "HI!"
CLASSIES = []
for i in xrange(4):
Classy.CLASSIES.append(Classy())
However, I'd suggest you first think long and hard whether you actually need this effectively-global list, and whether you need it to be part of the class object. Personally, I almost never do something like this.
The simplest way to do this is do it after the class is created, when the class has been defined, and therefore can be used:
class Classy :
CLASSIES = []
def __init__(self) :
self.hi = "HI!"
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
(Here using a list comprehension for convinience, as it's the most readable and efficent way to build a list).
Also note that if this intended to be a constant, you should probably make it a tuple rather than a list, and if it isn't intended to be, you should probably not use an ALL_CAPS name which, by convention, implies a constant.
It seems to me that you want to obtain that:
class Classy :
CLASSIES = []
def __init__(self) :
self.hi = "HI!"
Classy.CLASSIES.append(self)
for i in xrange(4):
Classy()
for x in Classy.CLASSIES:
print x
result
<__main__.Classy instance at 0x011DF3F0>
<__main__.Classy instance at 0x011DF440>
<__main__.Classy instance at 0x011DF418>
<__main__.Classy instance at 0x011DF2B0>
EDIT
Note that with the code of Lattyware:
class Classy :
CLASSIES = []
idC = id(CLASSIES)
def __init__(self) :
self.hi = "HI!"
#Classy.CLASSIES.append(self)
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
print Classy.idC
print id(Classy.CLASSIES)
print 'Classy.idC==id(Classy.CLASSIES) :',Classy.idC==id(Classy.CLASSIES)
result
18713576
10755928
Classy.idC==id(Classy.CLASSIES) : False
While with the for loop of delnan'code, it doesn't appear.
However it's easy to correct:
writing
Classy.CLASSIES[:] = [Classy() for _ in xrange(0,4)]
or
Classy.CLASSIES.extend(Classy() for _ in xrange(0,4))
instead of
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
it depends of what is desired.
EDIT 2
Methods may reference global names in the same way as ordinary
functions. The global scope associated with a method is the module
containing its definition. (A class is never used as a global scope.)
http://docs.python.org/2/tutorial/classes.html#class-definition-syntax
A class has a namespace implemented by a dictionary object. Class
attribute references are translated to lookups in this dictionary,
e.g., C.x is translated to C.__dict__["x"]
http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes
class Classy :
CLASSIES = []
print '"CLASSIES" in globals()',"CLASSIES" in globals()
print '"CLASSIES" in Classy.__dict__ ==',"CLASSIES" in Classy.__dict__
result
"CLASSIES" in globals() False
"CLASSIES" in Classy.__dict__ == True
Delnan, how will you continue to pretend that CLASSIES is global ??
Did I misunderstood something in your debate with Lattyware ?
The class itself is not defined until after the class block finishes executing, so you can't make use of the class inside its own definition.
You could use a class decorator or a metaclass to add your desired class variable after the class is created. Here's an example with a decorator.
def addClassy(cls):
cls.CLASSIES = [cls() for a in xrange(4)]
return cls
#addClassy
class Classy(object):
pass
>>> Classy.CLASSIES
0: [<__main__.Classy object at 0x000000000289A240>,
<__main__.Classy object at 0x000000000289A518>,
<__main__.Classy object at 0x000000000289A198>,
<__main__.Classy object at 0x000000000289A208>]

How to keep track of class instances?

Toward the end of a program I'm looking to load a specific variable from all the instances of a class into a dictionary.
For example:
class Foo():
def __init__(self):
self.x = {}
foo1 = Foo()
foo2 = Foo()
...
Let's say the number of instances will vary and I want the x dict from each instance of Foo() loaded into a new dict. How would I do that?
The examples I've seen in SO assume one already has the list of instances.
One way to keep track of instances is with a class variable:
class A(object):
instances = []
def __init__(self, foo):
self.foo = foo
A.instances.append(self)
At the end of the program, you can create your dict like this:
foo_vars = {id(instance): instance.foo for instance in A.instances}
There is only one list:
>>> a = A(1)
>>> b = A(2)
>>> A.instances
[<__main__.A object at 0x1004d44d0>, <__main__.A object at 0x1004d4510>]
>>> id(A.instances)
4299683456
>>> id(a.instances)
4299683456
>>> id(b.instances)
4299683456
#JoelCornett's answer covers the basics perfectly. This is a slightly more complicated version, which might help with a few subtle issues.
If you want to be able to access all the "live" instances of a given class, subclass the following (or include equivalent code in your own base class):
from weakref import WeakSet
class base(object):
def __new__(cls, *args, **kwargs):
instance = object.__new__(cls, *args, **kwargs)
if "instances" not in cls.__dict__:
cls.instances = WeakSet()
cls.instances.add(instance)
return instance
This addresses two possible issues with the simpler implementation that #JoelCornett presented:
Each subclass of base will keep track of its own instances separately. You won't get subclass instances in a parent class's instance list, and one subclass will never stumble over instances of a sibling subclass. This might be undesirable, depending on your use case, but it's probably easier to merge the sets back together than it is to split them apart.
The instances set uses weak references to the class's instances, so if you del or reassign all the other references to an instance elsewhere in your code, the bookkeeping code will not prevent it from being garbage collected. Again, this might not be desirable for some use cases, but it is easy enough to use regular sets (or lists) instead of a weakset if you really want every instance to last forever.
Some handy-dandy test output (with the instances sets always being passed to list only because they don't print out nicely):
>>> b = base()
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> class foo(base):
... pass
...
>>> f = foo()
>>> list(foo.instances)
[<__main__.foo object at 0x0000000002606898>]
>>> list(base.instances)
[<__main__.base object at 0x00000000026067F0>]
>>> del f
>>> list(foo.instances)
[]
You would probably want to use weak references to your instances. Otherwise the class could likely end up keeping track of instances that were meant to have been deleted. A weakref.WeakSet will automatically remove any dead instances from its set.
One way to keep track of instances is with a class variable:
import weakref
class A(object):
instances = weakref.WeakSet()
def __init__(self, foo):
self.foo = foo
A.instances.add(self)
#classmethod
def get_instances(cls):
return list(A.instances) #Returns list of all current instances
At the end of the program, you can create your dict like this:
foo_vars = {id(instance): instance.foo for instance in A.instances}
There is only one list:
>>> a = A(1)
>>> b = A(2)
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x100587250>]
>>> id(A.instances)
4299861712
>>> id(a.instances)
4299861712
>>> id(b.instances)
4299861712
>>> a = A(3) #original a will be dereferenced and replaced with new instance
>>> A.get_instances()
[<inst.A object at 0x100587290>, <inst.A object at 0x1005872d0>]
You can also solve this problem using a metaclass:
When a class is created (__init__ method of metaclass), add a new instance registry
When a new instance of this class is created (__call__ method of metaclass), add it to the instance registry.
The advantage of this approach is that each class has a registry - even if no instance exists. In contrast, when overriding __new__ (as in Blckknght's answer), the registry is added when the first instance is created.
class MetaInstanceRegistry(type):
"""Metaclass providing an instance registry"""
def __init__(cls, name, bases, attrs):
# Create class
super(MetaInstanceRegistry, cls).__init__(name, bases, attrs)
# Initialize fresh instance storage
cls._instances = weakref.WeakSet()
def __call__(cls, *args, **kwargs):
# Create instance (calls __init__ and __new__ methods)
inst = super(MetaInstanceRegistry, cls).__call__(*args, **kwargs)
# Store weak reference to instance. WeakSet will automatically remove
# references to objects that have been garbage collected
cls._instances.add(inst)
return inst
def _get_instances(cls, recursive=False):
"""Get all instances of this class in the registry. If recursive=True
search subclasses recursively"""
instances = list(cls._instances)
if recursive:
for Child in cls.__subclasses__():
instances += Child._get_instances(recursive=recursive)
# Remove duplicates from multiple inheritance.
return list(set(instances))
Usage: Create a registry and subclass it.
class Registry(object):
__metaclass__ = MetaInstanceRegistry
class Base(Registry):
def __init__(self, x):
self.x = x
class A(Base):
pass
class B(Base):
pass
class C(B):
pass
a = A(x=1)
a2 = A(2)
b = B(x=3)
c = C(4)
for cls in [Base, A, B, C]:
print cls.__name__
print cls._get_instances()
print cls._get_instances(recursive=True)
print
del c
print C._get_instances()
If using abstract base classes from the abc module, just subclass abc.ABCMeta to avoid metaclass conflicts:
from abc import ABCMeta, abstractmethod
class ABCMetaInstanceRegistry(MetaInstanceRegistry, ABCMeta):
pass
class ABCRegistry(object):
__metaclass__ = ABCMetaInstanceRegistry
class ABCBase(ABCRegistry):
__metaclass__ = ABCMeta
#abstractmethod
def f(self):
pass
class E(ABCBase):
def __init__(self, x):
self.x = x
def f(self):
return self.x
e = E(x=5)
print E._get_instances()
Another option for quick low-level hacks and debugging is to filter the list of objects returned by gc.get_objects() and generate the dictionary on the fly that way. In CPython that function will return you a (generally huge) list of everything the garbage collector knows about, so it will definitely contain all of the instances of any particular user-defined class.
Note that this is digging a bit into the internals of the interpreter, so it may or may not work (or work well) with the likes of Jython, PyPy, IronPython, etc. I haven't checked. It's also likely to be really slow regardless. Use with caution/YMMV/etc.
However, I imagine that some people running into this question might eventually want to do this sort of thing as a one-off to figure out what's going on with the runtime state of some slice of code that's behaving strangely. This method has the benefit of not affecting the instances or their construction at all, which might be useful if the code in question is coming out of a third-party library or something.
Here's a similar approach to Blckknght's, which works with subclasses as well. Thought this might be of interest, if someone ends up here. One difference, if B is a subclass of A, and b is an instance of B, b will appear in both A.instances and B.instances. As stated by Blckknght, this depends on the use case.
from weakref import WeakSet
class RegisterInstancesMixin:
instances = WeakSet()
def __new__(cls, *args, **kargs):
o = object.__new__(cls, *args, **kargs)
cls._register_instance(o)
return o
#classmethod
def print_instances(cls):
for instance in cls.instances:
print(instance)
#classmethod
def _register_instance(cls, instance):
cls.instances.add(instance)
for b in cls.__bases__:
if issubclass(b, RegisterInstancesMixin):
b._register_instance(instance)
def __init_subclass__(cls):
cls.instances = WeakSet()
class Animal(RegisterInstancesMixin):
pass
class Mammal(Animal):
pass
class Human(Mammal):
pass
class Dog(Mammal):
pass
alice = Human()
bob = Human()
cannelle = Dog()
Animal.print_instances()
Mammal.print_instances()
Human.print_instances()
Animal.print_instances() will print three objects, whereas Human.print_instances() will print two.
Using the answer from #Joel Cornett I've come up with the following, which seems to work. i.e. i'm able to total up object variables.
import os
os.system("clear")
class Foo():
instances = []
def __init__(self):
Foo.instances.append(self)
self.x = 5
class Bar():
def __init__(self):
pass
def testy(self):
self.foo1 = Foo()
self.foo2 = Foo()
self.foo3 = Foo()
foo = Foo()
print Foo.instances
bar = Bar()
bar.testy()
print Foo.instances
x_tot = 0
for inst in Foo.instances:
x_tot += inst.x
print x_tot
output:
[<__main__.Foo instance at 0x108e334d0>]
[<__main__.Foo instance at 0x108e334d0>, <__main__.Foo instance at 0x108e33560>, <__main__.Foo instance at 0x108e335a8>, <__main__.Foo instance at 0x108e335f0>]
5
10
15
20
(For Python)
I have found a way to record the class instances via the "dataclass" decorator while defining a class. Define a class attribute 'instances' (or any other name) as a list of the instances you want to record. Append that list with the 'dict' form of created objects via the dunder method __dict__. Thus, the class attribute 'instances' will record instances in the dict form, which you want.
For example,
from dataclasses import dataclass
#dataclass
class player:
instances=[]
def __init__(self,name,rank):
self.name=name
self.rank=rank
self.instances.append(self.__dict__)

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