Параметры конструктора по умолчанию в pyyaml

Я не смог выяснить, как это сделать, в документации PyYAML. Я хочу представить классы Python, которые я определил в YAML, и иметь значение по умолчанию, заданное параметру в конструкторе, если оно не указано в YAML. Например:

>>> 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'

Я ожидал, что это создаст объект Test с bar=3, но я предполагаю, что он обходит мой конструктор, когда создает объект. Если я включаю отображение для бара в YAML, все работает как положено:

>>> yaml.load("""
... --- !Test
... foo: 5
... bar: 42
... """)
Test(foo=5, bar=42)

Кто-нибудь знает, как я могу использовать значение по умолчанию?

Ответ 1

Я столкнулся с той же проблемой: yaml_tag по какой-то причине не работает. Поэтому я использовал альтернативный подход:

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

Выход:

[Test(foo=1, bar=3), Test(foo=10, bar=20)]

Ответ 2

На основе ответа александлуканина13. Здесь мой разрез.

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

Затем вы можете использовать его следующим образом:

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