2013-10-08 27 views
1

我有一個封裝具有某些特徵的網絡配置文件的對象。例如,我的檔案有連接並根據它,它可以或不可以有IP選項(靜態DHCP)。使用動態繼承類來指定對象的特徵

所以,我的第一次嘗試使用從字典延伸的正常類,並添加了一些輔助功能:

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方式是什麼?

提前致謝!

+0

我很喜歡這個關於設計的視頻:http://vimeo.com/26330100 – User

+0

@User很有意思。謝謝! – guide42

回答

0

我真的不知道你的動態繼承的意思,但我會寫這樣說:

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) 

像這樣的事情會是什麼,我喜歡。我不認爲有必要進入元類。

+0

「IP」類只是一個例子。該配置文件具有更多特徵,需要同時實施。 每個'Connection'值都有一個大類,所以我可以做'isinstance(profile,Wireless)和profile.is_adhoc()'。 另一個例子是,如果'IP'是'dhcp'或'IP6'是'dhcp'或'dhcp-noaddr',我從具有'profile.get_client'的'DHCP'類擴展。 – guide42

+0

你可以。我希望'profile.is_wireless()和profile.is_adhoc()更好。 – User