这是我最初的尝试,在发布问题之前。
保留它,因为它可能有助于解释目标。
如果想要修改现有的 LARGE 集合,而不是将数据复制到新集合中,它还有一些有用的代码。
(其他答案创建新集合。)
# ---------- StripNones.py Python 2.7 ----------
import collections, copy
# Recursively remove None, from list/tuple elements, and dict key/values.
# NOTE: Changes type of iterable to list, except for strings and tuples.
# NOTE: We don't RECURSE KEYS.
# When "beImmutable=False", may modify "data".
# Result may have different collection types; similar to "filter()".
def StripNones(data, beImmutable=True):
t = type(data)
if issubclass(t, dict):
return _StripNones_FromDict(data, beImmutable)
elif issubclass(t, collections.Iterable):
if issubclass(t, basestring):
# Don't need to search a string for None.
return data
# NOTE: Changes type of iterable to list.
data = [StripNones(x, beImmutable) for x in data if x is not None]
if issubclass(t, tuple):
return tuple(data)
return data
# Modifies dict, removing items whose keys are in keysToRemove.
def RemoveKeys(dict, keysToRemove):
for key in keysToRemove:
dict.pop(key, None)
# Recursively remove None, from dict key/values.
# NOTE: We DON'T RECURSE KEYS.
# When "beImmutable=False", may modify "data".
def _StripNones_FromDict(data, beImmutable):
keysToRemove = []
newItems = []
for item in data.iteritems():
key = item[0]
if None in item:
# Either key or value is None.
keysToRemove.append( key )
else:
# The value might change when stripped.
oldValue = item[1]
newValue = StripNones(oldValue, beImmutable)
if newValue is not oldValue:
newItems.append( (key, newValue) )
somethingChanged = (len(keysToRemove) > 0) or (len(newItems) > 0)
if beImmutable and somethingChanged:
# Avoid modifying the original.
data = copy.copy(data)
if len(keysToRemove) > 0:
# if not beImmutable, MODIFYING ORIGINAL "data".
RemoveKeys(data, keysToRemove)
if len(newItems) > 0:
# if not beImmutable, MODIFYING ORIGINAL "data".
data.update( newItems )
return data
# ---------- TESTING ----------
# When run this file as a script (instead of importing it):
if (__name__ == "__main__"):
from collections import OrderedDict
maxWidth = 100
indentStr = '. '
def NewLineAndIndent(indent):
return '\n' + indentStr*indent
#print NewLineAndIndent(3)
# Returns list of strings.
def HeaderAndItems(value, indent=0):
if isinstance(value, basestring):
L = repr(value)
else:
if isinstance(value, dict):
L = [ repr(key) + ': ' + Repr(value[key], indent+1) for key in value ]
else:
L = [ Repr(x, indent+1) for x in value ]
header = type(value).__name__ + ':'
L.insert(0, header)
#print L
return L
def Repr(value, indent=0):
result = repr(value)
if (len(result) > maxWidth) and \
isinstance(value, collections.Iterable) and \
not isinstance(value, basestring):
L = HeaderAndItems(value, indent)
return NewLineAndIndent(indent + 1).join(L)
return result
#print Repr( [11, [221, 222], {'331':331, '332': {'3331':3331} }, 44] )
def printV(name, value):
print( str(name) + "= " + Repr(value) )
print '\n\n\n'
data1 = ( 501, (None, 999), None, (None), 504 )
data2 = { 1:601, 2:None, None:603, 'four':'sixty' }
data3 = OrderedDict( [(None, 401), (12, 402), (13, None), (14, data2)] )
data = [ [None, 22, tuple([None]), (None,None), None], ( (None, 202), {None:301, 32:302, 33:data1}, data3 ) ]
printV( 'ORIGINAL data', data )
printV( 'StripNones(data)', StripNones(data) )
print '----- beImmutable = True -----'
#printV( 'data', data )
printV( 'data2', data2 )
#printV( 'data3', data3 )
print '----- beImmutable = False -----'
StripNones(data, False)
#printV( 'data', data )
printV( 'data2', data2 )
#printV( 'data3', data3 )
print
输出:
ORIGINAL data= list:
. [None, 22, (None,), (None, None), None]
. tuple:
. . (None, 202)
. . {32: 302, 33: (501, (None, 999), None, None, 504), None: 301}
. . OrderedDict:
. . . None: 401
. . . 12: 402
. . . 13: None
. . . 14: {'four': 'sixty', 1: 601, 2: None, None: 603}
StripNones(data)= list:
. [22, (), ()]
. tuple:
. . (202,)
. . {32: 302, 33: (501, (999,), 504)}
. . OrderedDict([(12, 402), (14, {'four': 'sixty', 1: 601})])
----- beImmutable = True -----
data2= {'four': 'sixty', 1: 601, 2: None, None: 603}
----- beImmutable = False -----
data2= {'four': 'sixty', 1: 601}
要点:
if issubclass(t, basestring): 避免在字符串内部进行搜索,因为这没有任何意义,AFAIK。
if issubclass(t, tuple): 将结果转换回元组。
对于字典,使用copy.copy(data),返回与原始字典相同类型的对象。
限制:不尝试为以下类型保留集合/迭代器类型:list、tuple、dict(及其子类)。
如果需要更改,默认用法会复制数据结构。为False 传递beImmutable 可以在大量数据时产生更高的性能,但会改变原始数据,包括改变嵌套的数据片段——这可能会被代码中其他地方的变量引用。