Death to tuples!

Bengt Richter bokr at oz.net
Tue Nov 29 15:11:44 CET 2005


On 28 Nov 2005 14:48:35 GMT, Duncan Booth <duncan.booth at invalid.invalid> wrote:

>Antoon Pardon wrote:
>
>>> >>> def func(x):
>>> ...   if x in [1,3,5,7,8]:
>>> ...      print 'x is really odd'
>>> ...
>>> >>> dis.dis(func)
>>> ...
>>>    3          20 LOAD_FAST                0 (x)
>>>               23 LOAD_CONST               2 (1)
>>>               26 LOAD_CONST               3 (3)
>>>               29 LOAD_CONST               4 (5)
>>>               32 LOAD_CONST               5 (7)
>>>               35 LOAD_CONST               6 (8)
>>>               38 BUILD_LIST               5
>>>               41 COMPARE_OP               6 (in)
>> 
>> I'm probably missing something, but what would be the problem if this
>> list was created during compile time?
>
>Not much in this particular instance. 'x in aList' is implemented as 
>aList.__contains__(x), so there isn't any easy way to get hold of the 
>list[*] and keep a reference to it. On the other hand:
>
>def func(x):
>    return x + [1, 3, 5, 7, 8]
>
>we could pass in an object x with an add operator which gets hold of its 
>right hand operand and mutates it.
>
>So the problem is that we can't just turn any list used as a constant into 
>a constant list, we need to be absolutely sure that the list is used only 
>in a few very restricted circumstances, and since there isn't actually any 
>benefit to using a list here rather than a tuple it hardly seems 
>worthwhile.
>
>There might be some mileage in compiling the list as a constant and copying 
>it before use, but you would have to do a lot of timing tests to be sure.
>
>[*] except through f.func_code.co_consts, but that doesn't count.

If we had a way to effect an override of a specific instance's attribute accesses
to make certain attribute names act as if they were defined in type(instance), and
if we could do this with function instances, and if function local accesses would
check if names were one of the ones specified for the function instance being called,
then we could define locally named constants etc like properties.

The general mechanism would be that instance.__classvars__ if present would make
instance.x act like instance.__classvars__['x'].__get__(instance). IOW as if descriptor
access for descriptors defined in type(instance). Thus if you wrote
    instance.__classvars__ = dict(t=property(lambda self, t=__import__('time').ctime:t())) 

then that would make instance.t act as if you had assigned the property to type(instance).t
-- which is handy for types whose instances don't let you assign to type(instance).

This gets around to instances of the function type. Let's say we had a decorator
defined like

    def deco(f):
        f.__classvars__ = dict(now= property(lambda f, t=__import__('time').ctime:t()))
        return f

then if function local name access acted like self.name access where self was the function
instance[1], and type(self) was checked for descriptors, meaning in this case foo.__classvars__
would get checked like type(self).__dict__, then when you wrote

    @deco
    def foo():
        print now  # => print foo.__classvars__['now'].__get__(foo, type(foo))
                   #    (i.e., as if property defined as type(self) attribute where self is foo
                   #     but not accessed via global name as in above illustrative expression)
        now = 42   # => AttributeError: can't set attribute

This would also let you add properties to other unmodifiable types like the module type you see
via type(__import__('anymod')), e.g.,

    import foomod
    foomod.__classvars__ = dict(ver=property(lambda mod: '(Version specially formatted): 'r'%mod.version)
    # (or foomod could define its own __classvars__)
then
    foomod.ver
would act like a property defined in the __classvars__ - extended class variable namespace, and return
what a normal property would. Plain functions in foomod.__classvars__ would return bound methods with
foomod in "self" position, so you could call graphics.draw(args) and know that draw if so designed
could be defined as def draw(mod, *args): ...

Just another idea ;-)
[1] actually, during a function call, the frame instance if probably more like the "self", but let's
say the name resolution order for local access extends to foo.__classvars__ somethow as if that were
an overriding front end base class dict of the mro chain.

Regards,
Bengt Richter



More information about the Python-list mailing list