class variables for subclasses tuple

A

alainpoint

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
 
P

Peter Otten

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 __metaclass__(type):
.... x = property(lambda cls: 0)
.... x = property(lambda self: self[0])
....0

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

Peter
 
A

alainpoint

Peter said:
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 __metaclass__(type):
... x = property(lambda cls: 0)
... x = property(lambda self: self[0])
...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
 
A

alainpoint

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
 
P

Peter Otten

Peter said:
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:
.... class __metaclass__(type):
.... pass
.... for index, name in enumerate(names):
.... setattr(__metaclass__, name, property(lambda cls,
index=index: index))
.... del index
.... del name
........ 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
 
A

alainpoint

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

Peter Otten

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top