Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default constructor parameters in pyyaml

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?

like image 577
Colin Avatar asked Aug 28 '11 21:08

Colin


People also ask

What is a YAML constructor?

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!

What is PyYAML used for?

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.

What is parameter constructor in Python?

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.


2 Answers

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)]
like image 80
alexanderlukanin13 Avatar answered Oct 02 '22 14:10

alexanderlukanin13


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
like image 28
slowreader239 Avatar answered Oct 02 '22 15:10

slowreader239