【问题标题】:How to get attributes of parent class object only如何仅获取父类对象的属性
【发布时间】:2019-11-14 21:57:06
【问题描述】:

我有 2 节课:

class Parent(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name
        self.__parent_vars = ['id', 'name']  # make a copy

    def print_values(self):
        res = {}
        for el in self.__parent_vars:
            res[el] = vars(self)[el]
        return res


class Child(Parent):
    def __init__(self, id, name, last_name, age):
        Parent.__init__(self, id, name)
        self.last_name = last_name
        self.age = age

我想做的是从Child 类的Parent 参数中获取。我使用额外的变量制作它并且它可以工作,但我需要更优雅的解决方案而不需要额外的变量。我需要它来上泡菜课。如果我创建额外的变量,它会破坏我在一个大项目中的架构。

我试图找到这样的东西:

c = Child(12,"Foo","whatever",34)
vars(c.super())

预期输出:

{'id': 12, 'name': 'Foo'}

我发现了这个问题:Get attributibutes in only base class (Python) 但它和我的有很大的不同,所以我不能使用那个解决方案。

【问题讨论】:

  • 它们是通过分配给self创建的,这已经是Child。属性不会跟踪创建它们的函数。至少在某种程度上,您需要继续明确定义哪些属性属于父级。 (self.__parent_vars = list(self.__dict__) 可能是一个选项。)
  • @Ry- 感谢您的回复!这是否意味着不可能?一些super 提示或其他什么?或者关于创建一些额外的方法而不是字段的一些提示?
  • vars(c) 打印:{'id': 12, 'name': 'Foo', '_Parent__parent_vars': ['id', 'name'], 'last_name': 'whatever', 'age': 34} 而不是 c.print_values() {'id': 12, 'name': 'Foo'}
  • 也许在 Child 中,您应该创建新的 print_values,它使用 super().print_values() 来执行父 print_values,然后将子值添加到父值。
  • @MatteoPeluso 是的,当然 :) 这就是我问这个问题的原因)

标签: python python-3.x class oop inheritance


【解决方案1】:

恐怕你不能轻易。在 Python 中,类只携带方法和静态属性。非 静态属性通常存储在对象的__dict__ 属性中。这意味着除非在特殊情况下,您无法轻易知道在父类方法、子类方法甚至任何方法之外分配了哪些属性。

我只能想象一个元类,它将检测__init__ 方法来存储在其调用期间更改了哪些属性:

import collections
import functools
import inspect

class Meta_attr(type):
    init_func = {}
    attrs = collections.defaultdict(set)

    def __new__(cls, name, bases, namespace, **kwds):
        c = type.__new__(cls, name, bases, namespace, **kwds)
        cls.init_func[c] = c.__init__
        @functools.wraps(c.__init__)
        def init(self, *args, **kwargs):
            before = set(self.__dict__.keys())
            cls.init_func[c](self, *args, **kwargs)
            after = set(self.__dict__.keys())
            cls.attrs[c].update(after.difference(before))
        init.__signature__ = inspect.signature(c.__init__)
        c.__init__ = init
        return c

class Parent(object, metaclass=Meta_attr):
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def print_values(self):
        res = {}
        for el in Meta_attr.attrs[Parent]:
            res[el] = vars(self)[el]
        return res


class Child(Parent):
    def __init__(self, id, name, last_name, age):
        Parent.__init__(self, id, name)
        self.last_name = last_name
        self.age = age

它给出:

>>> c = Child(1,"a","b", 20)
>>> c.print_values()
{'id': 1, 'name': 'a'}

注意:如果属性设置在__init__ 方法之外,则不会被此元类注册...

【讨论】:

  • 感谢您的回答!正如我所见,它解决了问题,但看起来很容易使用附加文件__parent_vars:D
  • @Mikhail_Sam:元类是一个非常强大的工具,可以深入到内部 Python 机器中。它通常在大型框架中用于为用户定义的类提供一致的 API,但我不确定这里是否值得。只有你能这么说……
  • 我找到了另一种解决方案。请看一下。
【解决方案2】:

我找到了另一种解决方案。它看起来更简单,所以我使用它。它基于使用Marshmallow Schemas. 此外,它在我的项目中更有用,因为我已经使用了棉花糖模式。 这个想法:我创建了 2 个类和 2 个不同的模式。并且可以仅使用父模式序列化更大的类(子类):

代码

定义 2 个类:

class Parent(object):
    def __init__(self):
        self.id = 'ID'
        self.name = 'some_name'

class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        self.last_name = 'last_name'
        self.age = 15

from marshmallow import Schema, fields, EXCLUDE

定义 2 个模式:

class ParentSchema(Schema):
    ba = Parent()
    id = fields.Str(missing=ba.id)
    name = fields.Str(missing=ba.name)

class ChildSchema(Schema):
    ba = Child()
    id = fields.Str(missing=ba.id)
    name = fields.Str(missing=ba.name)

    last_name = fields.Str(missing=ba.last_name)
    age = fields.Int(missing=ba.age)

使用它只获取子对象的父属性:

user_data = {'id':'IDIDID', 'name':'add_name', 'last_name':'FAMILIYA'}
res = ParentSchema().load(user_data, unknown=EXCLUDE)

# output:
res
{'name': 'add_name', 'id': 'IDIDID'}

【讨论】:

    猜你喜欢
    • 2017-01-25
    • 2016-07-15
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    相关资源
    最近更新 更多