Movatterモバイル変換


[0]ホーム

URL:


Welcome, guest|Sign In|My Account|Store|Cart
ActiveState Code »Recipes
LanguagesTagsAuthorsSets

Nested contexts -- a chain of mapping objects(Python recipe)by Raymond Hettinger
ActiveState Code (http://code.activestate.com/recipes/577434/)

Easy to use chain of dictionaries for crafting nested scopes or for a tree of scopes. Useful for analyzing AST nodes, XML nodes or other structures with multiple scopes. Can emulate various chaining styles including static/lexical scoping, dynamic scoping and Python's own globals(), locals(), nested scopes, and writeable nonlocals. Can also model Python's inheritance chains: instance dictionary, class dictionary, and base classes.

Python, 152 lines
Copy to clipboard
  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
'Nested contexts trees for implementing nested scopes (static or dynamic)'fromcollectionsimportMutableMappingfromitertoolsimportchain,imapclassContext(MutableMapping):''' Nested contexts -- a chain of mapping objects.    c = Context()           Create root context    d = c.new_child()       Create nested child context. Inherit enable_nonlocal    e = c.new_child()       Child of c, independent from d    e.root                  Root context -- like Python's globals()    e.map                   Current context dictionary -- like Python's locals()    e.parent                Enclosing context chain -- like Python's nonlocals    d['x']                  Get first key in the chain of contexts    d['x'] = 1              Set value in current context    del['x']                Delete from current context    list(d)                 All nested values    k in d                  Check all nested values    len(d)                  Number of nested values    d.items()               All nested items    Mutations (such as sets and deletes) are restricted to the current context    when "enable_nonlocal" is set to False (the default).  So c[k]=v will always    write to self.map, the current context.    But with "enable_nonlocal" set to True, variable in the enclosing contexts    can be mutated.  For example, to implement writeable scopes for nonlocals:        nonlocals = c.parent.new_child(enable_nonlocal=True)        nonlocals['y'] = 10     # overwrite existing entry in a nested scope    To emulate Python's globals(), read and write from the the root context:        globals = c.root        # look-up the outermost enclosing context        globals['x'] = 10       # assign directly to that context    To implement dynamic scoping (where functions can read their caller's    namespace), pass child contexts as an argument in a function call:        def f(ctx):            ctx.update(x=3, y=5)            g(ctx.new_child())        def g(ctx):            ctx['z'] = 8                    # write to local context            print ctx['x'] * 10 + ctx['y']  # read from the caller's context    '''def__init__(self,enable_nonlocal=False,parent=None):'Create a new root context'self.parent=parentself.enable_nonlocal=enable_nonlocalself.map={}self.maps=[self.map]ifparentisnotNone:self.maps+=parent.mapsdefnew_child(self,enable_nonlocal=None):'Make a child context, inheriting enable_nonlocal unless specified'enable_nonlocal=self.enable_nonlocalifenable_nonlocalisNoneelseenable_nonlocalreturnself.__class__(enable_nonlocal=enable_nonlocal,parent=self)@propertydefroot(self):'Return root context (highest level ancestor)'returnselfifself.parentisNoneelseself.parent.rootdef__getitem__(self,key):forminself.maps:ifkeyinm:breakreturnm[key]def__setitem__(self,key,value):ifself.enable_nonlocal:forminself.maps:ifkeyinm:m[key]=valuereturnself.map[key]=valuedef__delitem__(self,key):ifself.enable_nonlocal:forminself.maps:ifkeyinm:delm[key]returndelself.map[key]def__len__(self,len=len,sum=sum,imap=imap):returnsum(imap(len,self.maps))def__iter__(self,chain_from_iterable=chain.from_iterable):returnchain_from_iterable(self.maps)def__contains__(self,key,any=any):returnany(keyinmforminself.maps)def__repr__(self,repr=repr):return' -> '.join(imap(repr,self.maps))if__name__=='__main__':c=Context()c['a']=1c['b']=2d=c.new_child()d['c']=3print'd:  ',dassertrepr(d)=="{'c': 3} -> {'a': 1, 'b': 2}"e=d.new_child()e['d']=4e['b']=5print'e:  ',eassertrepr(e)=="{'b': 5, 'd': 4} -> {'c': 3} -> {'a': 1, 'b': 2}"f=d.new_child(enable_nonlocal=True)f['d']=4f['b']=5print'f:  ',fassertrepr(f)=="{'d': 4} -> {'c': 3} -> {'a': 1, 'b': 5}"printlen(f)assertlen(f)==4assertlen(list(f))==4assertall(kinfforkinf)assertf.root==c# dynanmic scoping exampledeff(ctx):printctx['a'],'f:  reading "a" from the global context'print'f: setting "a" in the global context'ctx['a']*=999print'f: reading "b" from globals and setting "c" in locals'ctx['c']=ctx['b']*50print'f: ',ctxg(ctx.new_child())print'f: ',ctxdefg(ctx):print'g: setting "d" in the local context'ctx['d']=44print'''g: setting "c" in f's context'''ctx['c']=-1print'g: ',ctxglobal_context=Context(enable_nonlocal=True)global_context.update(a=10,b=20)f(global_context.new_child())

Easy to use:

>>> d = Context()     # Create a new context>>> d['k'] = 1        # Store a key/value just like a dict>>> e = d.new_child() # Create a child context>>> e['q'] = 2        # Store an item in the child context>>> e['k']            # Look-up a key in the child, then parent1>>> e                 # Display the dictionary chain{'q': 2} -> {'k': 1}

5 comments

Sunjay Varma13 years, 6 months ago # |flag

That's cool.

Alia Khouri13 years, 6 months ago # |flag

Nice!

thom neale13 years, 2 months ago # |flag

Really cool recipe.

Perhaps a useful enhancement would be the possibility of passing in a dict or extra_context mapping when calling Context() or c.new_child().

Miki Tebeka11 years, 3 months ago # |flag

I think__setitem__ and__delitem__ should raise KeyError if the key is not found.Also IMO__len__ since it'll count keys shared by parent and child twice. Should be something like:

return len(set(chain.from_iterable(m.iterkeys() for m in self.maps)))
Miki Tebeka11 years, 3 months ago # |flag

The above should read:... __len__ is wrong since ...

Created byRaymond HettingeronThu, 21 Oct 2010(MIT)
Python recipes (4591)
Raymond Hettinger's recipes (97)
HongxuChen's Fav (39)

Required Modules

Other Information and Tasks

 

Accounts

Code Recipes

Feedback & Information

ActiveState

Privacy Policy |Contact Us |Support

© 2024 ActiveState Software Inc. All rights reserved. ActiveState®, Komodo®, ActiveState Perl Dev Kit®, ActiveState Tcl Dev Kit®, ActivePerl®, ActivePython®, and ActiveTcl® are registered trademarks of ActiveState. All other marks are property of their respective owners.

 x  

[8]ページ先頭

©2009-2025 Movatter.jp