Core Bag FAQ

Frequently asked questions about core Bag functionality.

Basic Usage

How do I check if a key exists?

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1, 'b': 2})
>>> 'a' in bag
True
>>> 'z' in bag
False

What happens when I access a missing key?

Returns None (doesn’t raise KeyError):

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1})
>>> bag['missing']  # Returns None
>>> bag['missing'] is None
True

How do I get a default value for missing keys?

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1})
>>> value = bag['missing']
>>> result = value if value is not None else 'default'
>>> result
'default'

Or use get_item() with default:

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1})
>>> bag.get_item('missing', default='fallback')
'fallback'

How do I iterate over a Bag?

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1, 'b': 2})

>>> # Yields BagNode objects
>>> for node in bag:
...     print(node.label, node.value)
a 1
b 2

>>> # Dict-like methods
>>> list(bag.keys())
['a', 'b']
>>> list(bag.values())
[1, 2]
>>> list(bag.items())
[('a', 1), ('b', 2)]

Paths

Can I use slashes instead of dots?

No, only dots are supported as path separators:

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag['a.b.c'] = 1  # Correct
>>> bag['a.b.c']
1

What characters are allowed in labels?

Labels should be valid Python identifiers or strings. Avoid dots in labels:

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag['my_label'] = 1       # OK
>>> bag['label123'] = 2       # OK
>>> bag['my-label'] = 3       # OK (but underscore preferred)

How do I handle labels with dots?

If you need a literal dot in a label, use set_item() with a list path:

>>> from genro_bag import Bag

>>> bag = Bag()
>>> # This creates nested structure a -> b
>>> bag['a.b'] = 1

>>> # To get a literal 'a.b' label, you'd need to avoid the path syntax
>>> bag.set_item('version_1.0', 'value')  # Label is 'version_1'

Attributes

What’s the difference between value and attributes?

  • Value: The main data (bag['key'])

  • Attributes: Metadata about the node (bag['key?attr'])

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag.set_item('price', 99.99, currency='USD', taxable=True)

>>> bag['price']  # The value
99.99
>>> bag['price?currency']  # Metadata
'USD'

Can I have attributes without a value?

Yes, use None as the value:

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag.set_item('config', None, env='production', version='1.0')

>>> bag['config']  # No value
>>> bag['config?env']
'production'

How do I get all attributes of a node?

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag.set_item('item', 'value', a=1, b=2, c=3)

>>> node = bag.get_node('item')
>>> node.attr
{'a': 1, 'b': 2, 'c': 3}

Serialization

Why do numbers become strings in XML?

XML has no native type system. Everything is text:

>>> from genro_bag import Bag

>>> bag = Bag({'count': 42})
>>> xml = bag.to_xml()
>>> restored = Bag.from_xml(f'<r>{xml}</r>')
>>> restored['r.count']
'42'
>>> type(restored['r.count'])
<class 'str'>

Use TYTX for type preservation.

Which format should I use?

Need

Format

Human-readable config

XML

Web API exchange

JSON

Full Python types

TYTX JSON

Compact storage

TYTX MessagePack

How do I preserve types in round-trip?

Use TYTX:

>>> from genro_bag import Bag
>>> from decimal import Decimal

>>> bag = Bag()
>>> bag['price'] = Decimal('19.99')
>>> bag['count'] = 42

>>> restored = Bag.from_tytx(bag.to_tytx())
>>> type(restored['price'])
<class 'decimal.Decimal'>
>>> type(restored['count'])
<class 'int'>

Performance

Is Bag faster than dict?

No. Bag adds overhead for features like paths, attributes, and subscriptions. For simple key-value storage, use dict.

Bag is designed for:

  • Hierarchical data

  • Rich metadata

  • Serialization needs

  • Change tracking

How large can a Bag be?

Bag can handle thousands of nodes. For very large datasets (millions of items), consider:

  • Lazy loading with resolvers

  • Database backends

  • Streaming serialization

How do I copy a Bag efficiently?

>>> from genro_bag import Bag

>>> original = Bag({'a': 1, 'b': 2})

>>> # Deep copy via TYTX
>>> copy = Bag.from_tytx(original.to_tytx())
>>> copy['c'] = 3

>>> 'c' in original
False
>>> 'c' in copy
True

Common Mistakes

Forgetting that nested assignment creates structure

>>> from genro_bag import Bag

>>> bag = Bag()
>>> bag['a.b.c'] = 1

>>> # 'a' and 'a.b' are now Bags, not values
>>> isinstance(bag['a'], Bag)
True

Using = instead of set_item() when you need attributes

>>> from genro_bag import Bag

>>> bag = Bag()

>>> # This only sets value, no attributes
>>> bag['item'] = 'value'

>>> # This sets value AND attributes
>>> bag.set_item('item2', 'value', attr1='x', attr2='y')
>>> bag['item2?attr1']
'x'

Expecting KeyError on missing keys

>>> from genro_bag import Bag

>>> bag = Bag({'a': 1})

>>> # Returns None, doesn't raise
>>> bag['missing']
>>> bag['missing'] is None
True