Suggesting for overloading the assign operator

Michael Chermside mcherm at mcherm.com
Tue Jul 1 14:33:49 EDT 2003


Rim writes:
> I have been thinking about how to overload the assign operation '='.
      [...]
> the
> second assignment below ereases all Currency knowledge from variable
> 'c', when the user only wanted to update the value of c, not its type:
> 
> c=Currency(5)
> c=7
> 
> I fully understand the workaround of doing c=Curreny(7) instead, but
> users don't want to be bothered with that.

I think you have a fundamental misunderstanding of how assignment works
in Python. In some languages (C for example) variables are things that
have a TYPE and a VALUE. You typically set the TYPE by declaring the
variable and typically can't change it. You typically set the VALUE
by assignment. In at least one case (C++ is the only example I know of)
you can override the meaning of assignment to variables of a certain
type.

In Python, variables do NOT have a type and a value. Instead, variables
have a single value, which is always some object. Objects have a type
(and sometimes a class also... I'm not going to worry about this
difference). But there's little connection between variables and types.
When we assign (actually, a better term is "rebind") a variable it can
be assigned to any object.
    >>> x = "a string"
    >>> x = 5
    >>> x = Currency(7)

I'm confident that what you want to achieve can be accomplished in
Python, you just have to adjust your mindset. In Python it's useful to
think of things in terms of namespaces. (See last line of
"http://www.awaretek.com/zen.html".) Namespaces are just dictionaries
-- and they're used throughout Python. When you write
    >>> x = "abc"
you are simply performing  __setitem__("x", "abc") on a namespace called
the "module globals". If you want, you can get ahold of this dictionary
via the function globals(), and you can see that manipulating it is just
the same as using "=".
    >>> x = 'a string'
    >>> theGlobals = globals()
    >>> print theGlobals
    {'__builtins__': <module '__builtin__' (built-in), '__name__':
    '__main__', 'theGlobals': {...}, '__doc__': None, 'x': 'a stri
    ng'}
    >>> theGlobals["y"] = 7
    >>> print y
    7
Function definition is just another operation on this namespace...
writing "def func(x): return x + 1" is (almost) the same as 
"globals()["x"] = lambda x: x + 1". When you use local variables 
within a function, you are manipulating another namespace, (for 
performance purposes it's not actually implemented as a dictionary,
but it acts as if it were... just call locals() to see it as a dict).

So when you say that you want your users to be able to do this:
    >>> x = Currency(5)
    >>> x = 7
and then have x be a Currency object, what you're *really* saying is
that you want to take over control of the namespace that your users
are working in so that they can't store just anything in it, but
are forced to store only Currency objects. Or perhaps you mean for
them to be able to store only Currency objects for any key which
was previously used for a Currency object. Or maybe you mean that
if they try storing a NUMBER in a slot which was previously used to
store a Currency object that a new Currency object should be stored
instead. Actually, I'm not quite certain what your requirements are,
but it's pretty clear to me that what you want is a special namespace
with custom behavior.

Well, Python provides special namespaces with customizable
behavior. They're called classes. Classes are just another namespace
("one honking great idea")... well, technically TWO namespaces:
one for the class itself and one for the individual object. Normally,
saying "myObject.x = 7" performs another dictionary assignment
(this dictionary is accessible as "myobject.__dict__"), but you can
use the special functions __getattr__, __setattr__, __delattr_, and
perhaps __getattribute__ to customize its behavior.

So I'm guessing that you can accomplish your objective by getting
your users to store their values as fields in an object and providing
customized behavior on that object. I was going to suggest that you
create a custom dict type (here's one that just prints stuff out
when modified... you could use any logic you wanted):

    >>> class NoisyDict(dict):
    ...     def __setitem__(self, key, value):
    ...         print 'setting %s to %r' % (key, value)
    ...         dict.__setitem__(self, key, value)
    ...

and that you then use it in an exec statement, like this:

    >>> exec "x = 7\nprint x" in NoisyDict()
    7

Unfortunately, it didn't work (that printed "7", but didn't print
"setting x to 7"), so you may need a slightly better guru than me
to get this working. Or if you can better describe your PARTICULAR
problem, I may be able to figure out how to approach it. But I'm
almost certain that there's a much easier way than changing how 
Python does assignments.

-- Michael Chermside






More information about the Python-list mailing list