Instantiating sub-class from super
DL Neil
PythonList at DancesWithMice.info
Mon Oct 14 20:43:59 EDT 2019
Hi Greg,
On 15/10/19 11:37 AM, Gregory Ewing wrote:
> DL Neil wrote:
>> Is there a technique or pattern for taking a (partially-) populated
>> instance of a class, and re-creating it as an instance of one of its
>> sub-classes?
>
> Often you can assign to the __class__ attribute of an instance
> to change its class.
>
> Python 3.7.3 (default, Apr 8 2019, 22:20:19)
> [GCC 4.2.1 (Apple Inc. build 5664)] on darwin
> Type "help", "copyright", "credits" or "license" for more information.
> >>> class Person:
> ... pass
> ...
> >>> class Male(Person):
> ... pass
> ...
> >>> p = Person()
> >>> p.__class__ = Male
> >>> isinstance(p, Male)
> True
> >>>
Brilliantly easy. Thanks!
Is this manipulation documented/explained anywhere? Would you describe
it as 'good practice', or even: sensible?
> You would then be responsible for initialising any attributes of
> Male that Person didn't have.
Understood.
The contents of Person.__init__( -whatever- ) are executed at instantiation.
The contents of Male.__init__( - whatever- ) will be executed during a
'normal' instantiation.
If, however, a Person-instance is 'converted'* into a Male-instance,
then Male.__init__() will not be executed.
(haven't bothered to experiment with an explicit call, because...)
In this case, that is not an issue, because apart from the value of
"sex", the __init__() actions are all provided by super().
Further experimentation revealed something I wasn't sure to expect. Thus
although:
class Male( Person ):
etc
class Person():
etc
won't work, because Person has yet to be defined; in the correct
sequence, we *can* 'anticipate' names within the super-class:
class Person():
etc
def convert( self ):
self.__class__ = Male
class Male( Person ):
etc
Agreed, better is:
def convert( self, sub_class ):
self.__class__ = sub_class
...
p = Person()
p.convert( Male )
Amusingly enough, could "convert"* the p-instance again-and-again.
* careful terminology!
PoC code:
class Person():
def __init__( self ):
self.ID = "Respondent"
self.converter = { "M":Male, "F":Female }
def questionnaire( self, sex ):
self.sex = sex
def super_function( self ):
print( "Pulling on tights and donning cape..." )
def convert( self ):
self.__class__ = self.converter[ self.sex ]
class Male( Person ):
def male_only( self ):
print( "I am secure in my man-hood" )
class Female( Person ):
def female_only( self ):
print( "Thy name is vanity" )
p = Person()
print( "PersonOBJ ~", p )
print( "Person __class__ ~", p.__class__ )
print( "PersonID ~", p.ID )
p.questionnaire( "M" )
print( "M, MaleOBJ ~", p.sex, p.converter[ p.sex ] )
p.convert()
print( "PersonOBJ ~", p )
print( "Person __class__ ~", p.__class__ )
print( "PersonID ~", p.ID )
p.male_only()
p.super_function()
p.questionnaire( "F" )
print( "F, FemaleOBJ ~", p.sex, p.converter[ p.sex ] )
p.convert()
print( "PersonOBJ ~", p )
print( "Person __class__ ~", p.__class__ )
print( "PersonID ~", p.ID )
p.female_only()
p.super_function()
p.male_only() # Exception
Output:
Showing that:
- the object remains the same, even as its type/class is 'converted'
- the object retains any value established before 'conversion'
or, the 'conversion' process carries-across all data-attributes
- after conversion the sub-class's methods become available
- after conversion the super-class's methods remain available
- after a further 'conversion', the same experience
but methods particular to the first conversion have been 'lost'.
(as expected)
[~]$ python3 person.py
PersonOBJ ~ <__main__.Person object at 0x7f014085bdd0>
Person __class__ ~ <class '__main__.Person'>
PersonID ~ Respondent
M, MaleOBJ ~ M <class '__main__.Male'>
PersonOBJ ~ <__main__.Male object at 0x7f014085bdd0>
Person __class__ ~ <class '__main__.Male'>
PersonID ~ Respondent
I am secure in my man-hood
Pulling on tights and donning cape...
F, FemaleOBJ ~ F <class '__main__.Female'>
PersonOBJ ~ <__main__.Female object at 0x7f014085bdd0>
Person __class__ ~ <class '__main__.Female'>
PersonID ~ Respondent
Thy name is vanity
Pulling on tights and donning cape...
Traceback (most recent call last):
File "person.py", line 58, in <module>
p.male_only() # Exception
AttributeError: 'Female' object has no attribute 'male_only'
--
Regards =dn
More information about the Python-list
mailing list