49

Personプロパティとして複数のサブオブジェクト ( ) を持つオブジェクト ( )がありPet, Residenceます。これらのサブオブジェクトのプロパティを次のように動的に設定できるようにしたいと考えています。

class Person(object):
    def __init__(self):
        self.pet = Pet()
        self.residence = Residence()

class Pet(object):
    def __init__(self,name='Fido',species='Dog'):
        self.name = name
        self.species = species

class Residence(object):
    def __init__(self,type='House',sqft=None):
        self.type = type
        self.sqft=sqft


if __name__=='__main__':
    p=Person()
    setattr(p,'pet.name','Sparky')
    setattr(p,'residence.type','Apartment')
    print p.__dict__

現在、間違った出力が得られます:{'pet': <__main__.Pet object at 0x10c5ec050>, 'residence': <__main__.Residence object at 0x10c5ec0d0>, 'pet.name': 'Sparky', 'residence.type': 'Apartment'}

ご覧のとおり、 のサブオブジェクトにnameアトリビュートを設定する代わりに、 に新しいアトリビュートが作成されます。PetPersonpet.namePerson

  • 関連するキーが見つかった場合にテキストを解析し、オブジェクト属性を埋める同じメソッドによって異なるサブオブジェクトが設定されるため、person.petto を指定することはできません。setattr()

  • これを達成するための簡単な/組み込みの方法はありますか?

  • または、文字列を解析しgetattr()、必要なサブオブジェクトが見つかるまで複数回呼び出しsetattr()てから、見つかったサブオブジェクトを呼び出す再帰関数を作成する必要があるのでしょうか?

4

11 に答える 11

107

使用できますfunctools.reduce

import functools

def rsetattr(obj, attr, val):
    pre, _, post = attr.rpartition('.')
    return setattr(rgetattr(obj, pre) if pre else obj, post, val)

# using wonder's beautiful simplification: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects/31174427?noredirect=1#comment86638618_31174427

def rgetattr(obj, attr, *args):
    def _getattr(obj, attr):
        return getattr(obj, attr, *args)
    return functools.reduce(_getattr, [obj] + attr.split('.'))

rgetattrandは、ドット付き文字列も処理できるandrsetattrのドロップイン置換です。getattrsetattrattr


import functools

class Person(object):
    def __init__(self):
        self.pet = Pet()
        self.residence = Residence()

class Pet(object):
    def __init__(self,name='Fido',species='Dog'):
        self.name = name
        self.species = species

class Residence(object):
    def __init__(self,type='House',sqft=None):
        self.type = type
        self.sqft=sqft

def rsetattr(obj, attr, val):
    pre, _, post = attr.rpartition('.')
    return setattr(rgetattr(obj, pre) if pre else obj, post, val)

def rgetattr(obj, attr, *args):
    def _getattr(obj, attr):
        return getattr(obj, attr, *args)
    return functools.reduce(_getattr, [obj] + attr.split('.'))

if __name__=='__main__':
    p = Person()
    print(rgetattr(p, 'pet.favorite.color', 'calico'))
    # 'calico'

    try:
        # Without a default argument, `rgetattr`, like `getattr`, raises
        # AttributeError when the dotted attribute is missing
        print(rgetattr(p, 'pet.favorite.color'))
    except AttributeError as err:
        print(err)
        # 'Pet' object has no attribute 'favorite'

    rsetattr(p, 'pet.name', 'Sparky')
    rsetattr(p, 'residence.type', 'Apartment')
    print(p.__dict__)
    print(p.pet.name)
    # Sparky
    print(p.residence.type)
    # Apartment
于 2015-07-02T01:49:55.813 に答える
1

これは ChaimG の回答に似たものですが、任意の数のケースで機能します。ただし、属性の取得のみをサポートし、設定はサポートしていません。

requested_attr = 'pet.name'
parent = Person()

sub_names = requested_attr.split('.')
sub = None

for sub_name in sub_names:

    try:
        sub = parent.__getattribute__(sub_name)
        parent = sub

    except AttributeError:
        raise Exception("The panel doesn't have an attribute that matches your request!")

pets_name = sub
于 2020-06-18T21:51:57.203 に答える
1

上記の受け入れられた回答をありがとう。役に立ちました。誰かが使用を拡張したい場合はhasattr、以下のコードを使用してください。

def rhasattr(obj, attr):
    _nested_attrs = attr.split(".")
    _curr_obj = obj
    for _a in _nested_attrs[:-1]:
        if hasattr(_curr_obj, _a):
            _curr_obj = getattr(_curr_obj, _a)
        else:
            return False
    return hasattr(_curr_obj, _nested_attrs[-1])
于 2021-04-28T15:34:33.830 に答える