By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
429,570 Members | 999 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 429,570 IT Pros & Developers. It's quick & easy.

class variables for subclasses tuple

P: n/a
Hello,

I have got a problem that i can't readily solve.
I want the following:
I want to create a supertuple that behaves both as a tuple and as a
class.
It should do the following:
Point=superTuple("x","y","z") # this is a class factory
p=Point(4,7,9)
assert p.x==p[0]
assert p.y==p[1]
assert p.z==p[2]
I already found a recipe to do that (recipe 6.7 in the Python
cookbook). I reproduce the code hereunder:
def superTuple(*attribute_names):
" create and return a subclass of `tuple', with named attributes "
# make the subclass with appropriate _ _new_ _ and _ _repr_ _
specials
typename='Supertuple'
nargs = len(attribute_names)
class supertup(tuple):
_ _slots_ _ = ( ) # save memory, we don't need
per-instance dict
def _ _new_ _(cls, *args):
if len(args) != nargs:
raise TypeError, '%s takes exactly %d arguments (%d
given)' % (
typename, nargs, len(args))
return tuple._ _new_ _(cls, args)
def _ _repr_ _(self):
return '%s(%s)' % (typename, ', '.join(map(repr, self)))
# add a few key touches to our new subclass of `tuple'
for index, attr_name in enumerate(attribute_names):
setattr(supertup, attr_name, property(itemgetter(index)))
supertup._ _name_ _ = typename
return supertup

Now my problem is: i would like to extend this supertuple with class
variables so that i can obtain the following:
assert Point.x==0
assert Point.y==1
assert Point.z==2
while still having:
assert p.x==p[0]
assert p.y==p[1]
assert p.z==p[2]
This is not the case unfortunately:
Point.x=0 leads to having p.x==0
It seems not possible to have class variables and instance variable
having the same name and yet different values.
Alain

Mar 8 '06 #1
Share this Question
Share on Google+
6 Replies


P: n/a
al********@yahoo.fr wrote:
Point.x=0 leads to having p.x==0
It seems not possible to have class variables and instance variable
having the same name and yet different values.


A quick check:
class T(tuple): .... class __metaclass__(type):
.... x = property(lambda cls: 0)
.... x = property(lambda self: self[0])
.... t = T("abc")
t.x 'a' T.x

0

So possible it is. Come back if you're stuck generalizing the above.

Peter

Mar 8 '06 #2

P: n/a

Peter Otten wrote:
al********@yahoo.fr wrote:
Point.x=0 leads to having p.x==0
It seems not possible to have class variables and instance variable
having the same name and yet different values.


A quick check:
class T(tuple): ... class __metaclass__(type):
... x = property(lambda cls: 0)
... x = property(lambda self: self[0])
... t = T("abc")
t.x 'a' T.x

0

So possible it is. Come back if you're stuck generalizing the above.

Peter


Thanks for your magic answer.
But i am not so good at magic ;-)
If i want to generalize to a arbitrary number of variables, i got
syntax errors.
Within a class, you can only method/class definitions and assignments.
It is therefore difficult to do something like:
for idx, attr_name in enumerate(attribute_names):
setattr(__metaclass__,attr_name, property(lambda cls:idx)
for idx, attr_name in enumerate(attribute_names):
setattr(T,attr_name, property(lambda self:self[idx])

Alain

Mar 8 '06 #3

P: n/a
As an supplement to my previous post, please find hereunder a snippet
for my unsuccessful attempt (commented out snippet does not work):
def superTuple(*attribute_names):
nargs = len(attribute_names)
class T(tuple):
def __new__(cls, *args):
return tuple.__new__(cls, args)
class __metaclass__(type):
x=property(lambda self:0)
y=property(lambda self:1)
z=property(lambda self:2)
x=property(lambda self:self[0])
y=property(lambda self:self[1])
z=property(lambda self:self[2])
#for idx, attr_name in enumerate(attribute_names):
# print 'attr name',attr_name, idx
# setattr(T.__metaclass__,attr_name, property(lambda cls:idx))
#for idx, attr_name in enumerate(attribute_names):
# print 'attr name',attr_name
# setattr(T,attr_name, property(lambda self:self[idx]))
return T
if __name__ == '__main__':
Point=superTuple('x','y','z')
p=Point(4,7,9)
assert p.x==p[0]
assert p.y==p[1]
assert p.z==p[2]
assert Point.x==0
assert Point.y==1
assert Point.z==2

Alain

Mar 8 '06 #4

P: n/a
al********@yahoo.fr wrote:

Peter Otten wrote:
al********@yahoo.fr wrote:
> Point.x=0 leads to having p.x==0
> It seems not possible to have class variables and instance variable
> having the same name and yet different values.
A quick check:
>>> class T(tuple):

... class __metaclass__(type):
... x = property(lambda cls: 0)
... x = property(lambda self: self[0])
...
>>> t = T("abc")
>>> t.x

'a'
>>> T.x

0

So possible it is. Come back if you're stuck generalizing the above.

Peter


Thanks for your magic answer.
But i am not so good at magic ;-)


Once I grokked that a class is just an instance of its metaclass all magic
magically vanished :-)
If i want to generalize to a arbitrary number of variables, i got
syntax errors.
Within a class, you can only method/class definitions and assignments.
It is therefore difficult to do something like:
for idx, attr_name in enumerate(attribute_names):
setattr(__metaclass__,attr_name, property(lambda cls:idx) for idx, attr_name in enumerate(attribute_names):
setattr(T,attr_name, property(lambda self:self[idx])

Alain


I'm not getting syntax errors:
names = "xyz"
class T(tuple): .... class __metaclass__(type):
.... pass
.... for index, name in enumerate(names):
.... setattr(__metaclass__, name, property(lambda cls,
index=index: index))
.... del index
.... del name
.... for index, name in enumerate(names):

.... setattr(T, name, property(lambda self, index=index: self[index]))
....
Traceback (most recent call last):
File "<stdin>", line 2, in ?
AttributeError: can't set attribute
However, the read-only property of the metaclass prevents setting the class
attribute. A workaround is to set the class properties /before/ the
metaclass properties. Here is a no-frills implementation, mostly untested:

from operator import itemgetter

def constgetter(value):
def get(self):
return value
return get

def make_tuple(*names):
class TupleType(type):
pass

class T(tuple):
__metaclass__ = TupleType
def __new__(cls, *args):
if len(names) != len(args):
raise TypeError
return tuple.__new__(cls, args)
for index, name in enumerate(names):
setattr(T, name, property(itemgetter(index)))

for index, name in enumerate(names):
setattr(TupleType, name, property(constgetter(index)))

return T

Peter

Mar 8 '06 #5

P: n/a
Thank you Peter, this does the job.
In passing, I have another question: where can I read up more on
metaclasses?
Alain

Mar 8 '06 #6

P: n/a
al********@yahoo.fr wrote:
In passing, I have another question: where can I read up more on
metaclasses?


Well, in "Python in a Nutshell" Alex Martelli manages to pack the practical
information that lets you work with metaclasses into just four pages,
including a two-page example. You may have seen posts by Alex on c.l.py
that are longer...

Peter
Mar 8 '06 #7

This discussion thread is closed

Replies have been disabled for this discussion.