I haven't been able to find out how to do this in the PyYAML documentation. I want to represent python classes I've defined in YAML, and have a default value given to a parameter in the constructor if it's not specified in the YAML. For example:
>>> class Test(yaml.YAMLObject):
...     yaml_tag = u"!Test"
...     def __init__(self, foo, bar=3):
...             self.foo = foo
...             self.bar = bar
...     def __repr__(self):
...             return "%s(foo=%r, bar=%r)" % (self.__class__.__name__, self.foo, self.bar)
... 
>>> yaml.load("""
... --- !Test
... foo: 5
... """)
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 7, in __repr__
AttributeError: 'Test' object has no attribute 'bar'
I expected that it would create a Test object with bar=3, but I guess it bypasses my constructor when it creates the object. If I include a mapping for bar in the YAML, everything works as expected:
>>> yaml.load("""
... --- !Test
... foo: 5
... bar: 42
... """)
Test(foo=5, bar=42)
Does anyone know how I can have it use a default value?
Constructors and Representers From a high-level, a constructor allows you to take a YAML node and return a class instance; a representer allows you to serialize a class instance into a YAML node; and a tag helps PyYaml know which constructor or representer to call!
PyYAML is a YAML parser and emitter for Python. Using the PyYAML module, we can perform various actions such as reading and writing complex configuration YAML files, serializing and persisting YMAL data. Use it to convert the YAML file into a Python dictionary.
parameterized constructor: constructor with parameters is known as parameterized constructor. The parameterized constructor takes its first argument as a reference to the instance being constructed known as self and the rest of the arguments are provided by the programmer.
I encountered the same problem: yaml_tag doesn't work for some reason. So I used alternative approach:
import yaml
def constructor(loader, node) :
    fields = loader.construct_mapping(node)
    return Test(**fields)
yaml.add_constructor('!Test', constructor)
class Test(object) :
    def __init__(self, foo, bar=3) :
        self.foo = foo
        self.bar = bar
    def __repr__(self):
        return "%s(foo=%r, bar=%r)" % (self.__class__.__name__, self.foo, self.bar)
print yaml.load("""
- !Test { foo: 1 }
- !Test { foo: 10, bar: 20 }""")
Output:
[Test(foo=1, bar=3), Test(foo=10, bar=20)]
                        Based on alexanderlukanin13's answer. Here's my cut.
import yaml
YAMLObjectTypeRegistry = {}
def register_type(target):
    if target.__name__ in YAMLObjectTypeRegistry:
        print "{0} already in registry.".format(target.__name__)
    elif 'yaml_tag' not in target.__dict__.keys():
        print target.__dict__
        raise TypeError("{0} must have yaml_tag attribute".format(
            target.__name__))
    elif target.__dict__['yaml_tag'] is None:
        pass
    else:
        YAMLObjectTypeRegistry[target.__name__] = target
        yaml.add_constructor(
                target.__dict__['yaml_tag'],
                lambda loader, node: target(**loader.construct_mapping(node)))
        print "{0} added to registry.".format(target.__name__)
class RegisteredYAMLObjectType(type):
    def __new__(meta, name, bases, class_dict):
        cls = type.__new__(meta, name, bases, class_dict)
        register_type(cls)
        return cls
class RegisteredYAMLObject(object):
    __metaclass__=RegisteredYAMLObjectType
    yaml_tag = None
You can then use it like this:
class MyType(registry.RegisteredYAMLObject):
    yaml_tag = u'!mytype'
    def __init__(self, name, attr1='default1', attr2='default2'):
        super(MyType, self).__init__()
        self.name = name
        self.attr1 = attr1
        self.attr2 = attr2
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With