string formatting with mapping & '*'... is this a bug?

Bengt Richter bokr at oz.net
Sat Sep 11 00:15:32 EDT 2004


On Fri, 10 Sep 2004 23:39:26 +0200, aleaxit at yahoo.com (Alex Martelli) wrote:

>Pierre Fortin <pfortin at pfortin.com> wrote:
>   ...
>> > >          self.__dict__.update(locals())
>> > 
>> > Ah, sorry, my favourite idiom to avoid the boilerplate of
>> > 
>> >     def __init__(self, fee, fie, foo, fum):
>> >         self.fee = fee
>> >         self.fie = fie
>> >         self.foo = foo
>> >         self.fum = fum
>> > 
>> > Boilerplate is bad, and I'm keen on "Once, and ONLY once!" as a
>> > programming practice, so having to mention each of those names three
>> > times in totally repetitive ways makes me shiver.  My favourite idiom
>> 
>> I fully agree with "once and ONLY once"...  but you've pointed out that
>> "not-at-all is better than once"... :^)
>
>Well, there is 'once' here -- the names of the attributes are listed
>once, in the argument list of 'def __init__'
>
>> > does, per se, leave a silly self.self around (which means a silly
>> > reference loop) so I should remember to 'del self.self' after it...
>> 
>> Since "self" is a convention (could use "this"), t'would be nice if Python
>> could avoid foo.foo ref. loops in a future release...
>
>I don't see how to do that cleanly, simply and within general rules.
>"Special cases are not special enough to break the rules"...
>

You could use a helper function to strip out the name, which you know
anyway if you know to write this.__dict__ or self.__dict__, e.g.,

 >>> def updd(firstname, locd):
 ...     sansfirst = locd.copy()
 ...     del sansfirst[firstname]
 ...     locd[firstname].__dict__.update(sansfirst)
 ...
 >>> class C(object):
 ...     def __init__(a1,a2,a3): updd('a1',locals())
 ...
 >>> c=C(2,3)
 >>> vars(c)
 {'a3': 3, 'a2': 2}

The locd.copy etc could be replaced by not using update. Actually, you could
probably inherit from a class with a metaclass that would let you write
just def __init__(a1, a2, a3): pass and have all the methods' __init__s
appropriately modified.

BTW, do you know offhand why somedict.update(obj) seems to use obj.keys() and
obj.__getitem__ if obj has object as base, but not if it has dict as base?
Ran into that trying to wrap locals() to filter out self or whatever:

 >>> class D(dict):
 ...     def keys(self): return [1,2,3]
 ...     def __getitem__(self, key): return 2*key
 ...
 >>> dd = D({'a':123})
 >>> dd.keys()
 [1, 2, 3]
 >>> dd[5]
 10
 >>> dd
 {'a': 123}
 >>> d={}
 >>> d.update(dd)
 >>> d
 {'a': 123}

Ok, now this:
 >>> class E(object):
 ...     def keys(self): return [1,2,3]
 ...     def __getitem__(self, key): return 2*key
 ...     def __init__(self, *a): print a
 ...
 >>> ee = E({'a':123})
 ({'a': 123},)
 >>> ee.keys()
 [1, 2, 3]
 >>> ee[3]
 6
 >>> ee
 <__main__.E object at 0x009C72F0>
 >>> e={}
 >>> e.update(ee)
 >>> e
 {1: 2, 2: 4, 3: 6}

The methods are there and do override the keys and __getitem__ of dict:
 >>> dd.keys(), ee.keys()
 ([1, 2, 3], [1, 2, 3])
 >>> dd[5], ee[5]
 (10, 10)


But that d.update(dd) doesn't match what you'd think from
 ----
 >>> help({}.update)
 Help on built-in function update:

 update(...)
     D.update(E) -> None.  Update D from E: for k in E.keys(): D[k] = E[k]
 ----

I see
 >>> D.mro()
 [<class '__main__.D'>, <type 'dict'>, <type 'object'>]
 >>> E.mro()
 [<class '__main__.E'>, <type 'object'>]

Is there another method that update looks for first that serves update better
than keys() and __getitem__ so it ignores the latter? Presumably a method that
dict would have but not object?

ISTM I recall a similar problem with some builtin functions not calling overriding
subclass methods as one might expect?

Regards,
Bengt Richter



More information about the Python-list mailing list