Reputation: 3346
This is what I wish to do:
obj = object()
obj.(a,b,c) = 1,2,3
f(obj.(d,e))
This is what I know how to do:
obj = object()
obj.a, obj.b, obj.c = 1,2,3
Is there a syntax to do this in Python?
Follow-up questions:
("Why do you want to do this?" Because it feels intuitive to me to think about groups of member variables as a package sometimes. There are classes for which it makes sense, like a fixed-dimensional point, or a complex number.)
Upvotes: 3
Views: 195
Reputation: 3346
Based on sweeneyrod answer, I've written a way to have somewhat similar syntax, taking a comma-delimited string and square brackets. Usage:
thing = myclass()
thing['x,y'] = 3,6
x,y = thing['x,y']
thing['x,y,z'] = 1,2,3
This gives
>>> x
3
>>> y
6
>>> thing.x
1
>>> thing.z
3
Class definition:
#user class
class myclass:
def __getitem__(self,keys):
keys = keys.split(',')
return tuple(getattr(self, attr) for attr in keys)
def __setitem__(self,keys,values):
keys = keys.split(',')
if len(keys) != len(values):
raise ValueError('too many values to unpack (expected %d)'%len(keys))
for attr,val in zip(keys, values):
setattr(self, attr, val)
def __delitem__(self,keys):
keys = keys.split(',')
for attr in keys:
delattr(self, attr)
Issues:
__getitem__
, if you need it.__setattr__
throw an exception?)I originally thought to use thing.grouping('x,y')=3,6
, but I forgot that I can't assign to a function call.
Decorator version:
try:
from itertools import izip as zip #python 2
except:
pass #python 3 already has zip
#decorator
def attribute_tuple(cls):
if not hasattr(attribute_tuple,'initialized'):
attribute_tuple.initialized=True
def get(self,keys):
keys = keys.split(',')
return tuple(getattr(self, attr) for attr in keys)
attribute_tuple.get = get
def set(self,keys,values):
keys = keys.split(',')
if len(keys) != len(values):
raise ValueError('too many values to unpack (expected %d)'%len(keys))
for attr,val in zip(keys, values):
setattr(self, attr, val)
attribute_tuple.set = set
def delete(self,keys):
keys = keys.split(',')
for attr in keys:
delattr(self, attr)
attribute_tuple.delete = delete
cls.__getitem__ = attribute_tuple.get
cls.__setitem__ = attribute_tuple.set
cls.__delitem__ = attribute_tuple.delete
return cls
@attribute_tuple
class myclass:
pass
Upvotes: 0
Reputation: 362837
It is not possible because of the grammar of attribute references.
An attribute reference is a primary followed by a period and a name:
attributeref ::= primary "." identifier
And an identifier must begin with a letter, not parentheses.
Identifiers (also referred to as names) are described by the following lexical definitions:
identifier ::= (letter|"_") (letter | digit | "_")*
letter ::= lowercase | uppercase
lowercase ::= "a"..."z"
uppercase ::= "A"..."Z"
digit ::= "0"..."9"
This means you will get a SyntaxError
no matter how much hacking you do on __getattr__
and/or __getattribute__
.
Upvotes: 1
Reputation: 11060
I don't think that there is exact syntax for that, but to me this feels similar:
class Thing:
def __init__(self, x, y):
self.point = (x, y)
def __getattr__(self, name):
if name == "x":
return self.point[0]
elif name == "y":
return self.point[1]
else:
raise AttributeError
def __setattr__(self, name, value):
if name == "x":
self.point = (value, self.point[1])
elif name == "y":
self.point = (self.point[0], value)
else:
object.__setattr__(self, name, value)
thing = Thing(4, 7)
thing.point = (3, 6)
thing.x = 5
Upvotes: 3
Reputation: 10360
There is no "member tuple" syntax of precisely the kind you're describing. Here is a possible workaround, though, for setting "tuples" of attributes and passing "tuples" of attributes. It's kind of bulky, so I don't know if I would actually want to use it, though.
class MyClass:
pass
def set_tuple(obj, **attrs):
for key in attrs:
setattr(obj, key, attrs[key])
def pass_tuple(obj, *attrnames):
return (getattr(obj, x) for x in attrnames)
def print_things(*iterable):
for x in iterable:
print(x)
obj = MyClass()
set_tuple(obj, a=1, b=2, c=3)
print(obj.a, obj.b, obj.c, '\n')
print_things(*pass_tuple(obj, 'a', 'c'))
Result:
1 2 3
1
3
Upvotes: 0