【问题标题】:Use dynamic inherit classes to specify characteristic of an object使用动态继承类来指定对象的特征
【发布时间】:2013-10-08 10:53:27
【问题描述】:

我有一个对象,它封装了具有某些特征的网络配置文件。例如,我的个人资料有一个 Connection 并且取决于它,它可能有也可能没有 IP 选项(static dhcp)。

所以,我的第一次尝试是使用从 dict 扩展而来的普通类并添加一些辅助函数:

class Profile(dict):
    IP_CONNECTIONS = ('ethernet', 'wireless', 'pppoe')
    def is_ethernet(self): return self['Connection'] == 'ethernet'
    def is_wireless(self): return self['Connection'] == 'wireless'
    def is_wireless_adhoc(self): return self.is_wireless() and 'AdHoc' in self
    def has_ip(self)
        return self['Connection'] in self.IP_CONNECTIONS
    def has_ip_static(self)
        if not self.has_ip():
            return False
        if self.is_ipv4():
            return self['IP'] == 'static'
        if self.is_ipv6():
            return self['IP6'] == 'static'
        return False
    def has_ip_dhcp(self):
        if not self.has_ip():
            return False
        if self.is_ipv4():
            return self['IP'] == 'dhcp'
        if self.is_ipv6():
            return self['IP6'] == 'dhcp' or self['IP6'] == 'dhcp-noaddr'
        return False
    def is_ipv4(self): return self.has_ip() and 'IP' in self
    def is_ipv6(self): return self.has_ip() and 'IP6' in self
    def get_client(self):
        if self.has_ip_dhcp() and 'DHCPClient' in self:
            return self['DHCPClient']
        return None

这行得通,但我有一个庞大的类,其中包含很多 is_*has_* 特征函数。它们中的大多数只会用于非常特定的配置文件,并且大部分时间会返回False

然后我突然想到我可以使用继承来描述特征。

在尝试并未能实现元类后,因为在调用 __new__ 方法时数据尚不可用。我想出了这样的事情:

def load_profile(filename):
    data = _read_profile(filename)
    bases = _classify_profile(data)
    baseclass = type('Profile', bases, {})
    return baseclass(data)

class IP:
    CONNECTIONS = ('ethernet', 'wireless')
class IPv4(IP):
    def is_static(self):
        return self['IP'] == 'static'
class IPv6(IP):
    def is_static(self):
        return self['IP6'] == 'static'
class DHCP:
    def get_client(self):
        return self['DHCPClient'] if 'DHCPClient' in self else None

class Wireless:
    def is_adhoc(self):
        return 'AdHoc' in self

def _classify_profile(data):
    classes = [dict]

    if data['Connection'] == 'wireless':
        classes.append(Wireless)
    if data['Connection'] in IP.CONNECTIONS:
        if 'IP' in data:
            classes.append(IPv4)
            if data['IP'] == 'dhcp':
                classes.append(DHCP)
        if 'IP6' in data:
            classes.append(IPv6)
            if data['IP6'] == 'dhcp' or data['IP6'] == 'dhcp-noaddr':
                classes.append(DHCP)

    return tuple(classes)

以前我在做profile.has_ip(),现在我只是用isinstance(profile, IP) 测试它。在我看来,通过良好的责任分离,这一点更加清晰。

问题:这是实现动态继承的好方法吗?这样做的pythonic方式是什么?

提前致谢!

【问题讨论】:

  • 我非常喜欢这个关于设计的视频:vimeo.com/26330100
  • @User 非常有趣。谢谢!

标签: python oop multiple-inheritance


【解决方案1】:

我真的不知道你所说的动态继承是什么意思,但我会这样写:

base_classes = []

class IP(dict):
    CONNECTIONS = ('ethernet', 'wireless')
    def is_static(self):
        raise NotImplementedError('To be implemented in subclasses.') 
    @classmethod
    def wants_the_meaningful_named_data(cls, data):
        return False
base_classes.append(IP)

class IPv4(IP):
    def is_static(self):
        return self['IP'] == 'static'
    @classmethod
    def wants_the_meaningful_named_data(cls, data):
        return data['Connection'] in cls.CONNECTIONS and 'IP' in data
base_classes.append(IPv4)

class IPv6(IP):
    def is_static(self):
        return self['IP6'] == 'static'
    @classmethod
    def wants_the_meaningful_named_data(cls, data):
        return data['Connection'] in cls.CONNECTIONS and 'IP6' in data
base_classes.append(IPv6)

def load_profile(filename):
    data = _read_profile(filename)
    for base_class in base_classes:
        if base_class.wants_the_meaningful_named_data(data):
            return base_class(data)
    return dict(data)

我喜欢这样的东西。我认为没有必要进入元类。

【讨论】:

  • IP 类只是一个示例。该配置文件具有更多需要同时实现的特性。每个“连接”值都有一个大类,所以我可以做isinstance(profile, Wireless) and profile.is_adhoc()。另一个例子是如果“IP”是“dhcp”或“IP6”是“dhcp”或“dhcp-noaddr”,我从具有profile.get_clientDHCP 类扩展。
  • 你可以。我希望profile.is_wireless() and profile.is_adhoc() 更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-02
  • 1970-01-01
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
相关资源
最近更新 更多