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

AltStyle によって変換されたページ (->オリジナル) /