cdxcore.pretty#

Classes

PrettyHierarchy([copy])

A cdxcore.pretty.PrettyObject which can easily create hierarchies of cdxcore.pretty.PrettyObject's.

PrettyObject([copy])

Class mimicing an ordered dictionary.

class cdxcore.pretty.PrettyHierarchy(copy=None, **kwargs)[source]#

Bases: PrettyObject

A cdxcore.pretty.PrettyObject which can easily create hierarchies of cdxcore.pretty.PrettyObject’s.

This works:

from cdxcore.pretty import PrettyHierarchy

r = PrettyHierarchy()
r.a = 1
r.x.b = 2

assert r.a == 1
assert isinstance(r.x,PrettyHierarchy)
assert r.x.b == 2

Some oddities:

from cdxcore.pretty import PrettyHierarchy

r = PrettyHierarchy()

r.A = 1
print( r.a )   # -> prints an empty PrettyHierarchy
_ = r.b        # generates an empty PrettyHierarchy
assert set(r) == {'A','a','b'} # all above created entries.

Runtime semantics can be confusing as cdxcore.pretty.PrettyHierarchy creates objects on the fly if an attrbute is not known. Hence typos can generate confusing error messages: assume we have some code that creates a cdxcore.pretty.PrettyHierarchy:

data = PrettyHierarchy()
data = ...
data.center = compute_centre()

Somewhere else we then access data.centre instread of data.center:

np.sum( data.centre )

This raises TypeError: 'PrettyHierarchy' object is not callable instead of an AttributeError.

Note that ["x"] keeps working as expected, i.e. it will fail if ‘x’ does not exist.

class cdxcore.pretty.PrettyObject(copy=None, **kwargs)[source]#

Bases: MutableMapping

Class mimicing an ordered dictionary.

Example:

from cdxcore.pretty import PrettyObject
pdct = PrettyObject()
pdct.x = 1
pdct['y'] = 2
print( pdct['x'], pdct.y ) # -> prints 1 2

The object mimics a dictionary:

print(pdct)  # -> '{'x': 1, 'y': 2}'

u = dict( pdct )
print(u)     # -> {'x': 1, 'y': 2}

u = { k: 2*v for k,v in pdct.items() }
print(u)     # -> {'x': 2, 'y': 4}

l = list( pdct ) 
print(l)     # -> ['x', 'y']

Important: attributes starting with ‘__’ cannot be accessed with item [] notation. In other words:

pdct = PrettyObject()
pdct.__x = 1    # fine
_ = pdct['__x'] # <- throws an exception

Access by Index Position

cdxcore.pretty.PrettyObject retains order of construction. To access its members by index position, use the cdxcore.pretty.PrettyObject.at_pos attribute:

print(pdct.at_pos[1])             # -> prints "2"
print(pdct.at_pos.keys[1])        # -> prints "y"
print(list(pdct.at_pos.items[2])) # -> prints "[('x', 1), ('y', 2)]"

Vectorized access

Several functions support accessing member elements using vectors, for example:

Setting elements:

from cdxcore.pretty import PrettyObject
r = PrettyObject()
r['a','b'] = 1,2
print(r.a,r.b)  # -> 1,2
r['a','b'] = (1,2)
print(r.a,r.b)  # -> 1,2

Reading elements:

r = PrettyObject(a=1,b=2)
a, b = r['a','b']
print(a,b)      # -> 1,2

Reading elements with defaults, classic method:

r = PrettyObject(a=1,b=2) a, b, c = r.get([‘a’,’b’,’c’],[1,2,33]) print(a,b,c) # -> 1,2,33

Reading elements with defaults, keyword methd

r = PrettyObject(a=1,b=2) a, b, c = r.get(a=11,b=22,c=33) print(a,b,c) # -> 1,2,33

Popping elements with defaults, keyword methd

r = PrettyObject(a=1,b=2) a, b, c = r.pop(a=11,b=22,c=33) print(a,b,c) # -> 1,2,33 assert len(r)==0

Same for cdxcore.pretty.PrettyObject.setdefault():

r = PrettyObject(a=1,b=2) a, b, c = r.setdefault(a=1,b=2,c=33) print(a,b,c) # -> 1,2,33 print(r.c) # -> 33

Assigning Member Functions

PrettyObject objects also allow assigning bona fide member functions by a simple semantic of the form:

pdct = PrettyObject(b=2)
pdct.mult_b = lambda self, x: self.b*x
pdct.mult_b(3) # -> 6

Calling pdct.mult_b(3) with above pdct will return 6 as expected. To assign static member functions, use the [] operator. The reason for this is as follows: consider:

def mult( a, b ):
    return a*b
pdct = PrettyObject()
pdct.mult = mult
pdct.mult(3,4) --> produces am error as three arguments must be passed: self, 3, and 4

In this case, use:

pdct = PrettyObject()
pdct['mult'] = mult
pdct.mult(3,4) --> 12

You can also pass member functions to the constructor:

p = PrettyObject( f=lambda self, x: self.y*x, y=2)
p.f(3) # -> 6

Operators

Objects of type cdxcore.pretty.PrettyObject support the following operators:

  • Comparison operator == and != test for equality of keys and values. Unlike for dictionaries comparisons are performed in in order. That means PrettyObject(x=1,y=2) and PrettyObject(y=2,x=1) are not equal.

  • Super/subset operators >= and <= test for a super/sup set relationship, respectively.

  • The a | b returns the union of two cdxcore.pretty.PrettyObject. Elements of the b overwrite any elements of a, if they are present in both. The order of the new dictionary is determined by the order of appearance of keys in first a and then b, that means in all but trivial cases a|b != b|a.

    The |= operator is a short-cut for cdxcore.pretty.PrettyObject.update().

Parameters:
copyMapping, optional

If present, assign elements of copy to self.

** kwargs:

Key/value pairs to be added to self.

as_field()[source]#

This function provides support for dataclasses.dataclass fields with PrettyObject default values.

When adding a field with a non-frozen default value to a @dataclass class, a default_factory has to be provided. The function as_field returns the corresponding dataclasses.Field element by returning simply:

def factory():
    return self
return dataclasses.field( default_factory=factory )

Usage is as follows:

from dataclasses import dataclass
@dataclass 
class A:
    data : PrettyDict = PrettyDict(x=2).as_field()

a = A() 
print(a.data.x)  # -> "2"
a = A(data=PrettyDict(x=3)) 
print(a.data.x)  # -> "3"
property at_pos#

Elementary access to the data contained in self by ordinal position. The ordinal position of an element is determined by the order of addition to the dictionary.

  • at_pos[position] returns an element or elements at an ordinal position:

    • It returns a single element if position refers to only one field.

    • If position is a slice then the respecitve list of fields is returned.

  • at_pos.keys[position] returns the key or keys at position.

  • at_pos.items[position] returns the tuple (key, element) or a list thereof for position.

You can also write data using the attribute notation:

  • at_pos[position] = item assigns an item to an ordinal position:

    • If position refers to a single element, item must be the value to be assigned to this element.

    • If position is a slice then ‘item must resolve to a list (or generator) of the required size.

clear()[source]#

Delete all elements.

copy(**kwargs)[source]#

Return a shallow copy; optionally add further key/value pairs.

get(__key__=None, default=<cdxcore.pretty.PrettyObject._No_Default_dummy object>, **keys)[source]#

Get element key with optional default default. Equivalent to dict.get(). Alternatively, this function takes a list of keys and their default values in dictionary notation in which case the read values are returned in order. You cannot mix using key and keys.

Standard usage

This function supports key being a sequence in which case this function returns a tuple of the same length with the respective results. The default value will be interpreted accordingly.

Hence, the following works:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.get( ['a', 'b','c'],[11,22,33] )
print(a,b,c) # -> 1,2,33        

Keyword usage

Provide keys with default values, i.e.:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.get( a=11, b=22, c=33 )
print(a,b,c) # -> 1,2,33                
items()[source]#

Equivalent to dict.items()

keys()[source]#

Equivalent to dict.keys()

no_default = <cdxcore.pretty.PrettyObject._No_Default_dummy object>#
pop(__key__=None, default=<cdxcore.pretty.PrettyObject._No_Default_dummy object>, **keys)[source]#

Get and remove element key with optional default default. Equivalent to dict.pop(). Alternatively, this function takes a list of keys and their default values in dictionary notation in which case the read values are returned in order. You cannot mix using key and keys.

Standard usage

This function supports key being a sequence in which case this function returns a tuple of the same length with the respective results. The default value will be interpreted accordingly.

Hence, the following works:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.pop( ['a', 'b','c'],[11,22,33] )
print(a,b,c) # -> 1,2,33

Keyword usage

Provide keys with default values, i.e.:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.pop( a=11, b=22, c=33 )
print(a,b,c) # -> 1,2,33
setdefault(__key__=None, default=None, **keys)[source]#

Returns the value for key or default if not found. In the latter case it adds default as value for key to the dictionary. Equivalent to dict.setdefault().

Alternatively, this function takes a list of keys and their default values in dictionary notation in which case the read values are returned in order. You cannot mix using key and keys.

Standard usage

This function supports key being a sequence in which case this function returns a tuple of the same length with the respective results. The default value will be interpreted accordingly.

Hence, the following works:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.setdefault( ['a', 'b','c'],[11,22,33] )
print(a,b,c) # -> 1,2,33        
print(r.c) # -> 33

Keyword usage

Provide keys with default values, i.e.:

from cdxcore.pretty import PrettyObject

r = PrettyObject(a=1, b=2)
a,b,c = r.setdefault( a=11, b=22, c=33 )
print(a,b,c) # -> 1,2,33
print(r.c) # -> 33
update(other=None, **kwargs)[source]#

Equivalent to dict.update().

Note that functon assignments are handled in normal dictionary fashion - in particular, bound functions will not become magically unbound.

values()[source]#

Equivalent to dict.values()