Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #21810 > unrolled thread

Using non-dict namespaces in functions

Started bySteven D'Aprano <steve+comp.lang.python@pearwood.info>
First post2012-03-17 11:18 +0000
Last post2012-03-18 08:50 +0100
Articles 4 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  Using non-dict namespaces in functions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-17 11:18 +0000
    Re: Using non-dict namespaces in functions Eric Snow <ericsnowcurrently@gmail.com> - 2012-03-17 11:42 -0700
      Re: Using non-dict namespaces in functions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-03-18 01:52 +0000
        Re: Using non-dict namespaces in functions Peter Otten <__peter__@web.de> - 2012-03-18 08:50 +0100

#21810 — Using non-dict namespaces in functions

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-17 11:18 +0000
SubjectUsing non-dict namespaces in functions
Message-ID<4f647317$0$29981$c3e8da3$5496439d@news.astraweb.com>
Inspired by the new collections.ChainMap in Python 3.3

http://docs.python.org/dev/library/collections.html#collections.ChainMap

I would like to experiment with similar non-dictionary namespaces in 
Python 3.2.

My starting point is these two recipes, adapted for Python 3.2:

http://code.activestate.com/recipes/305268/
http://code.activestate.com/recipes/577434/

Or for simplicity, here's a mock version:


from collections import Mapping
class MockChainMap(Mapping):
    def __getitem__(self, key):
        if key == 'a': return 1
        elif key == 'b': return 2
        raise KeyError(key)
    def __len__(self):
        return 2
    def __iter__(self):
        yield 'a'
        yield 'b'

Note that it is important for my purposes that MockChainMap does not 
inherit from dict.

Now I try to create a function that uses a MockChainMap instead of a dict 
for its globals:

function = type(lambda: None)
f = lambda x: (a+b+x)
g = function(f.__code__, MockChainMap(), 'g')

And that's where I get into trouble:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function() argument 2 must be dict, not MockChainMap


How do I build a function with globals set to a non-dict mapping?

If this can't be done, any suggestions for how I might proceed?


-- 
Steven

[toc] | [next] | [standalone]


#21828

FromEric Snow <ericsnowcurrently@gmail.com>
Date2012-03-17 11:42 -0700
Message-ID<mailman.764.1332009772.3037.python-list@python.org>
In reply to#21810
On Sat, Mar 17, 2012 at 4:18 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> Note that it is important for my purposes that MockChainMap does not
> inherit from dict.

Care to elaborate?

> Now I try to create a function that uses a MockChainMap instead of a dict
> for its globals:
>
> function = type(lambda: None)
> f = lambda x: (a+b+x)
> g = function(f.__code__, MockChainMap(), 'g')
>
> And that's where I get into trouble:
>
> Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
> TypeError: function() argument 2 must be dict, not MockChainMap
>
>
> How do I build a function with globals set to a non-dict mapping?
>
> If this can't be done, any suggestions for how I might proceed?

This looks like one of those cases where there is strict type checking
(of a Python object) at the C level.  You may consider bringing this
up in a tracker ticket.  Unless there are performance implications,
it's likely a case of no one having bothered to change this spot to be
more duck-type friendly.  There are quite a few of those in CPython
and I've seen at least a couple updated when someone brought it up.

Regardless, you could also implement __call__() on a function
look-alike class to get what you're after.  It may not be as
performant though.

-eric

[toc] | [prev] | [next] | [standalone]


#21845

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-03-18 01:52 +0000
Message-ID<4f653fd9$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#21828
On Sat, 17 Mar 2012 11:42:49 -0700, Eric Snow wrote:

> On Sat, Mar 17, 2012 at 4:18 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> Note that it is important for my purposes that MockChainMap does not
>> inherit from dict.
> 
> Care to elaborate?

I want to use collections.ChainMap, or something very like it, and I 
don't want to be forced into an unnatural is-a relationship with dict if 
I don't have to.


[...]
> Regardless, you could also implement __call__() on a function look-alike
> class to get what you're after.  It may not be as performant though.

I don't think that can work, because __call__ itself is a function, and I 
would need to change *its* globals. Which brings me back exactly where I 
started, trying to change globals in a function to a non-dict.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#21851

FromPeter Otten <__peter__@web.de>
Date2012-03-18 08:50 +0100
Message-ID<mailman.776.1332057058.3037.python-list@python.org>
In reply to#21845
Steven D'Aprano wrote:

> On Sat, 17 Mar 2012 11:42:49 -0700, Eric Snow wrote:
> 
>> On Sat, Mar 17, 2012 at 4:18 AM, Steven D'Aprano
>> <steve+comp.lang.python@pearwood.info> wrote:
>>> Note that it is important for my purposes that MockChainMap does not
>>> inherit from dict.
>> 
>> Care to elaborate?
> 
> I want to use collections.ChainMap, or something very like it, and I
> don't want to be forced into an unnatural is-a relationship with dict if
> I don't have to.
> 
> 
> [...]
>> Regardless, you could also implement __call__() on a function look-alike
>> class to get what you're after.  It may not be as performant though.
> 
> I don't think that can work, because __call__ itself is a function, and I
> would need to change *its* globals. Which brings me back exactly where I
> started, trying to change globals in a function to a non-dict.

The key lookup code in ceval.c is inlined, so even subclassing dict and 
overriding __getitem__() won't help. Instead of 

def f(a):
  return a + b # b taken from some custom namespace

you have to resort to the conventional approach

def f(a, ns=magic()):
    return a + ns["b"]

or

def f(self, a):
    return a + self.b

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web