The Cost of Dynamism (was Re: Pyhon 2.x or 3.x, which is faster?)

Steven D'Aprano steve at pearwood.info
Sat Mar 12 11:22:47 EST 2016


On Sat, 12 Mar 2016 01:20 pm, Chris Angelico wrote:


>>> Definitely agree with this. Having a way to declare that a name is
>>> "truly constant" would be extremely handy; there currently isn't a
>>> way, and I'm not sure whether FAT Python is looking into this or not.

"Constants" would be a new language feature, not an optimization. Unless
CPython adds a constant feature, FAT Python isn't going to do it.


>>> I think it's more focusing on the situation where something simply has
>>> never been rebound, which is probably the better optimization; but
>>> sometimes it'd be nice to be able to assert that something *will not*
>>> be rebound, to help with self-documenting code. (Note that "constant"
>>> implies both "never rebound" and "immutable".

I disagree that constant necessarily implies immutable, at least in the
context of Python. At least, what *I* want from a const keyword is a way to
make a certain name "write-once": you can bind to it the first time, and
then never again for the life of the scope. It shouldn't matter whether it
is a list or a float.


>> The 'const' prefix here is intended to define a named constant (a numeric
>> literal with a convenient alias) rather than some 'read-only attribute
>> for a conventional variable.
>>
>> But introducing that into Python would be a can of worms. (A named
>> constant needs a compile-time expression on the right-hand-side. The
>> compiler needs to be able to see named constants which are in an imported
>> module, and which are accessed via attributes, and so on.)

I disagree with that too. It is a limitation of older languages like Pascal
and C that they can only define "consts" at compile time. But we can
dynamically create constants at runtime too. Here's a proof-of-concept.

Let's put efficiency aside and consider a naive "bind-once" const for a
language like Python. Every namespace has a mapping of name:value, as we
have now, plus a list of "constant names". Then every binding operation:

   x = value
   import x
   from module import x
   del x
   def x(): ...
   class x: ...
   for x in seq: ...
   with expr as x: ...
   except error as x: ...

first checks the list of constant names for the name (e.g. "x"). If the name
is not found, then the binding operation is allowed, just like it is today.
If it is found, then the namespace is checked to see if the name already
exists. If it does, the operation raises a TypeError (or perhaps
ConstError?). If not, then the operation continues as normal.



> Not sure about that. Consider:
> 
> MAX_SIZE = 1<<16
> def some_func(blah):
>     data = some_file.read(MAX_SIZE)
> 
> Currently, this disassembles to show that MAX_SIZE is being fetched
> with LOAD_GLOBAL. If, instead, it were LOAD_CONST, this would mean
> that rebinding MAX_SIZE after function definition would fail; 

I don't think it would fail in the sense of raising an explicit exception. I
think it would just be hard to understand, giving you strange and
mysterious behaviour: there's a name which I can successfully rebind, but
some functions accessing it still see the old value, while others see the
new value.

But then, that's not far from the current behaviour of "early binding" of
default values.


> but it 
> would run faster. That's the advantage of a "declared constant", even
> without it being any sort of compile-time constant. As long as it can
> be finalized before the function is defined, it can be called
> constant.

Constants should be treated as constant even outside of functions.

Maybe the secret is to make modules more like functions in their internal
details?



-- 
Steven




More information about the Python-list mailing list