cdxcore.pretty#

A simple cdxcore.pretty.PrettyObject class which mimics directory access to its members.

Overview#

The purpose is a functional-programming style pattern for generating complex objects:

from cdxbasics.prettydict import PrettyObject
pdct = PrettyObject(z=1)

pdct.num_samples = 1000
pdct.num_batches = 100
pdct.method = "signature"

The object allows accessing members via []:

print( pdct[‘num_samples’] ) # -> 1000 print( pdct[‘num_batches’] ) # -> 100

Features#

cdxcore.pretty.PrettyObject implements all relevant dictionary protocols, so objects of type cdxcore.pretty.PrettyObject can (nearly always) be passed where dictionaries are expected:

  • A cdxcore.pretty.PrettyObject object supports standard dictionary semantics in addition to member attribute access. That means you can use pdct['num_samples'] as well as pdc.num_samples. You can mix standard dictionary notation with member attribute notation:

    print(pdct["num_samples"]) # -> prints "1000"
    pdct["test"] = 1           # sets pdct.test to 1     
    
  • Iterations work just like for dictionaries; for example:

    for k,v in pdct.items():
        print( k, v)
    
  • Applying str and repr to objects of type cdxcore.pretty.PrettyObject will return dictionary-type results, so for example print(pdct) of the above will return {'z': 1, 'num_samples': 1000, 'num_batches': 100, 'method': 'signature'}.

The cdxcore.pretty.PrettyObject.at_pos attribute allows accessing elements of the ordered dictionary by positon:

  • cdxcore.pretty.PrettyObject.at_pos[i] returns the i th element.

  • cdxcore.pretty.PrettyObject.at_pos.keys[i] returns the i th key.

  • cdxcore.pretty.PrettyObject.at_pos.items[i] returns the i th item.

For example:

print(pdct.at_pos[3])      # -> prints "signature"
print(pdct.at_pos.keys[3]) # -> prints "method"

You can also assign member functions to a cdxcore.pretty.PrettyObject. The following works as expected:

pdct.f = lambda self, y: return self.y*x

(to assign a static function which does not refer to self, use pdct['g'] = lambda z : return z).

Dataclasses#

dataclasses rely on default values of any member being “frozen” objects, which most user-defined objects and cdxcore.pretty.PrettyObject objects are not. This limitation applies as well to flax modules. To use non-frozen default values, use the cdxcore.pretty.PrettyObject.as_field() function:

from cdxbasics.prettydict import PrettyObject
from dataclasses import dataclass

@dataclass
class Data:
    data : PrettyObject = PrettyObject(x=2).as_field()

    def f(self):
        return self.data.x

d = Data()   # default constructor used.
d.f()        # -> returns 2

Import#

from cdxcore.pretty import PrettyObject as pdct

Documentation#

Classes

PrettyObject([copy])

Class mimicing an ordered dictionary.

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)]"

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.

Attributes:
at_pos

Elementary access to the data contained in self by ordinal position.

Methods

as_field()

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

clear()

Delete all elements.

copy(**kwargs)

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

get(key[, default])

Equivalent to dict.get().

items()

Equivalent to dict.items()

keys()

Equivalent to dict.keys()

pop(key[, default])

Equivalent to dict.pop().

popitem()

as a 2-tuple; but raise KeyError if D is empty.

setdefault(key[, default])

Equivalent to dict.setdefault().

update([other])

Equivalent to dict.update().

values()

Equivalent to dict.values()

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, default=<cdxcore.pretty.__No_Default_dummy object>)[source]#

Equivalent to dict.get().

items()[source]#

Equivalent to dict.items()

keys()[source]#

Equivalent to dict.keys()

pop(key, default=<cdxcore.pretty.__No_Default_dummy object>)[source]#

Equivalent to dict.pop().

setdefault(key, default=None)[source]#

Equivalent to dict.setdefault().

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()