[Python-Dev] Re: Simulating Class (was Re: Does Python have Class methods)
James_Althoff@i2.com
James_Althoff@i2.com
Fri, 18 May 2001 12:10:11 -0700
Python-dev'ers,
Pardon the intrusion, but Aahz Maruch suggested that I post this to the
python-dev list. The message below illustrates "yet another class method
recipe" that Costas synthesized (and which I then modified very slightly)
from various posts following another discussion on python-list about class
methods (as we all await the "type/class healing" stuff some of you are
working on -- go team!). This variant uses explicit "metaclasses" (defined
as regular classes) whose instances ("meta objects") point to class objects
(since they cannot *be* class objects in current Python). Anyway, I think
the approach has some nice properties.
Best regards,
Jim
----- Forwarded by James Althoff/AMER/i2Tech on 05/18/01 11:23 AM -----
James Althoff
To: python-list@python.org
05/14/01 02:09 cc:
PM Subject: Re: Simulating Class (was Re: Does Python have Class
methods)(Document link: James Althoff)
Costas writes:
>Ok, so after looking thru how Python works and comments from people, I
>came up with what I believe may be the best way to implement Class
>methods and Class variables.
>
><snip>
>
>Costas
I think this idea is quite good. I would amend it very slightly by
suggesting the convention of defining *three* separate names in the
enclosing module:
1) the name of the enclosing class
2) the name of the singleton instance of the enclosing class
3) the name of the enclosed class
To support this, I would propose using a naming convention as below.
If one is interested in defining a class Spam, then use the following
names:
1) SpamMetaClass -- names the enclosing class
2) SpamMeta -- names a singleton instance of the enclosing class
3) Spam -- names the enclosed class
Use the name SpamMetaClass when you need to derive a subclass of
SpamMetaClass, e.g.,
class SpecialSpamMetaClass(SpamMetaClass): pass
Use the name SpamMeta to invoke a class method, e.g.,
SpamMeta.aClassMethod()
Use the name Spam to make instances as usual, e.g.,
s = Spam()
(and to derive a subclass of Spam).
Although SpamMetaClass is not a metaclass in the sense of Smalltalk or Ruby
-- that is to say, the class Spam is not an instance of SpamMetaClass --
nonetheless, SpamMetaClass still acts as a "higher level" class that
provides methods on behalf of the class Spam where said methods are 1)
independent of any particular instance of Spam and 2) allow for
factory-method-style creation of Spam instances -- these being two very
important attributes of the metaclass concept. Plus "meta" is a nice,
short name. :-) Plus using "MetaClass" to refer to the class and "Meta"
to refer to the singleton instance of "MetaClass" is reasonably clear and
succinct, I think.
One nice thing about the proposed recipe is that the SpamMeta object is a
real class instance of a real class. This means that -- unlike when using
the "module function" recipe -- we get inheritance of methods, and --
unlike when using the "callable wrapper class" recipe -- we also get
override of methods.
The example below illustrates both of these important capabilities.
class Class1MetaClass: # Base metaclass
# Define "class methods" for Class1
def whoami(self):
print 'Class1MetaClass.whoami:', self
def new(self): # Factory method
"""Return a new instance"""
return self.Class1()
def newList(self,n=3): # Another factory method
"""Return a list of new instances"""
l = []
for i in range(n):
newInstance = self.new()
l.append(newInstance)
return l
# Define Class1 & its "instance methods"
class Class1: # Base class
def whoami(self):
print 'Class1.whoami:', self
Class1Meta = Class1MetaClass() # Make & name the singleton metaclass
instance
Class1 = Class1Meta.Class1 # Make the Class1 name accessible
class Class2MetaClass(Class1MetaClass): # Derived metaclass
# Define "class methods" for Class2 -- Override Class1 "class methods"
def whoami(self):
print 'Class2MetaClass.whoami:', self
def new(self): # Override the factory method
return self.Class2()
# Define Class2 & its "instance methods"
class Class2(Class1): # Derived class
def whoami(self):
print 'Class2.whoami:', self
Class2Meta = Class2MetaClass() # Make & name the singleton metaclass
instance
Class2 = Class2Meta.Class2 # Make the Class2 name accessible
# Test
Class1Meta.whoami() # invoke "class method" of base class
Class2Meta.whoami() # invoke "class method" of derived class
Class1().whoami() # make an instance & invoke "instance method"
Class2().whoami()
print Class1Meta.newList() # factory method
print Class2Meta.newList() # inherit factory method with override
>>> reload(meta6)
Class1MetaClass.whoami: <meta6.Class1MetaClass instance at 00810DBC>
Class2MetaClass.whoami: <meta6.Class2MetaClass instance at 00812D6C>
Class1.whoami: <meta6.Class1 instance at 0081058C>
Class2.whoami: <meta6.Class2 instance at 0081058C>
[<meta6.Class1 instance at 0081147C>, <meta6.Class1 instance at 0081151C>,
<meta6.Class1 instance at
0081009C>]
[<meta6.Class2 instance at 0081147C>, <meta6.Class2 instance at 00810CCC>,
<meta6.Class2 instance at
0081009C>]
<module 'meta6' from 'c:\_dev\python20\meta6.py'>
Jim