实例在构造后将属性发送到其他实例

时间:2014-09-01 08:03:04

标签: python-3.x instance instance-variables

我一直在寻找一种从instance-resp访问配置文件条目的方法。类绑定变量。因此,我创建了以下模块:

from ..lib.files import ConfigFile
from abc import abstractmethod

__all__ = ['ClassConfig',
           'InstanceConfig',
           'Configurable']

class ConfigEntry():
    """
    A Config entry
    """
    __value = None

    def __init__(self, value=None):
        """
        Initializes the 
        """
        self.__value = value

    def __set__(self, __, value):
        self.__value = value

    @property
    def value(self):
        """
        Returns the value
        """
        return self.__value


class ClassConfig(ConfigEntry):
    """
    A class config entry
    """
    def __get__(self, obj, cls):
        """
        Returns its value, when called by a class, else itself
        """
        if obj == None:
            return self.value
        else:
            return self


class InstanceConfig(ConfigEntry):
    """
    An instance config entry
    """
    def __get__(self, obj, cls):
        """
        Returns its value, when called by an instance, else itself
        """
        if obj != None:
            return self.value
        else:
            return self


class Configurable():
    """
    Configuration file binding
    """
    __SUFFIX = '.conf' 
    __TYPES = {int: 'int',
               float: 'float',
               str: 'str',
               bool: 'bool'}
    __file_ = None
    __lbi = '['
    __lei = ']'
    __ls = ','
    __ts = '←'
    __loaded = False

    def __init__(self, path, suffix=None):
        """
        Initialize the config file
        """
        # Initializes instance methods
        self.__setinstattr()
        suffix = suffix if suffix != None else self.__SUFFIX
        self.__file_ = ConfigFile(path + suffix)
        self.load()

    def __setinstattr(self):
        """
        Set instance attributes
        """
        self.__fields = self.__inst___fields
        self._file = self.__inst____file
        self._force_load = self.__inst__force_load
        self.load = self.__inst_load
        self.store = self.__inst_store

    @staticmethod        
    def __filter(attrs):
        return [a for a in attrs
                if a == a.upper() 
                and not a.startswith('_')]

    @staticmethod
    def __encode(val):
        """
        Encode a value
        """
        t = type(val)        
        if t == list:
            return Configurable.__lbi + \
                Configurable.__ls.join([Configurable.__encode(i) 
                              for i in val]) \
                    + Configurable.__lei 
        elif val == None:
            return None
        else:
            return Configurable.__ts.join([str(val),
                                           Configurable.__TYPES.get(t, '?')])

    @staticmethod
    def __decode(val):
        """
        Decode a value
        """
        def det_type(token):
            """
            Determine the type of a token
            """
            t = token.strip().split(Configurable.__ts)
            if len(t) == 2:
                raw_val = t[0]
                tpe = t[1]
                if tpe == Configurable.__TYPES[str]:
                    return str(raw_val)
                elif tpe == Configurable.__TYPES[int]:
                    return int(raw_val)
                elif tpe == Configurable.__TYPES[float]:
                    return float(raw_val)
                elif tpe == Configurable.__TYPES[bool]:
                    return True if raw_val.lower() in ['1', 
                                                       'true', 
                                                       't'] else False
                else:
                    try:
                        return int(raw_val)
                    except:
                        try:
                            return float(raw_val)
                        except:
                            return raw_val
            return token

        def str2list(s):
            """
            Try to parse a list from a string
            """
            def getlist(val):
                """
                Get a list from a reversed character list of a string
                """
                result = []
                token = ''
                while val:
                    c = val.pop()
                    if c == Configurable.__lei:
                        token = Configurable.__lei
                        result = [getlist(val)] + result
                    elif c == Configurable.__lbi:
                        if (not Configurable.__lbi in token) and (not Configurable.__lei in token):
                            result = [det_type(token)] + result
                        token = c
                        return result
                    elif c == Configurable.__ls:
                        if (not Configurable.__lbi in token) and (not Configurable.__lei in token):
                            result = [det_type(token)] + result
                        token = ''
                    else:
                        token = c + token
                if token:
                    result = [det_type(token)] + result
                return result
            l = []
            for char in s:
                l.append(char)
            l = getlist(l)
            if len(l) == 0:
                return l
            return l.pop()

        return str2list(val)

    @classmethod
    def __fields(cls):
        """
        Get fields for an instance
        """
        result = {}
        class Subclass(cls):
            def __init__(self):
                pass    
        instance = Subclass()
        attrs = Configurable.__filter(dir(instance))
        for a in attrs:
            aval = getattr(instance, a)
            if isinstance(aval, ClassConfig):
                value = getattr(cls, a)
                result[a] = value
        return result

    def __inst___fields(self):
        """
        Get fields of an instance
        """
        result = {}
        cls = self.__class__
        attrs = Configurable.__filter(dir(cls))
        for a in attrs:
            val = getattr(cls, a)
            if isinstance(val, InstanceConfig):
                value = getattr(self, a)
                result[a] = value
        return result

    @classmethod
    @abstractmethod
    def _file(cls):
        """
        Returns the file
        XXX: Implement by calling 
        super()._file(static_path)
        """
        pass

    @classmethod
    def _file_path(cls, path, suffix=None):
        """
        Returns the file relative to a path
        """
        suffix = suffix if suffix != None else cls.__SUFFIX
        f = ConfigFile(path + suffix)
        f.create()
        return f

    def __inst____file(self):
        """
        Returns the file
        """
        return self.__file_

    @classmethod
    def load(cls):
        """
        Loads the config file content, if not yet done into the class
        """
        if not cls.__loaded:
            return cls._force_load() 
        return True

    def __inst_load(self):
        """
        Loads the config file content, if not yet done into the instance
        """
        if not self.__loaded:
            return self._force_load() 
        return True

    @classmethod
    def _force_load(cls):
        """
        Loads the config file's content to the class
        """
        if cls._file().exists:
            data = cls._file().dict()
            for field in Configurable.__filter(data):
                setattr(cls, field, 
                        Configurable.__decode(data[field]))
            cls.__loaded = True
            return True
        return False

    def __inst__force_load(self):
        """
        Loads the config file's content to the instance
        """
        if self._file().exists:
            data = self._file().dict()
            for field in Configurable.__filter(data):
                setattr(self, field, 
                        Configurable.__decode(data[field]))
            self.__loaded = True
            return True
        return False

    @classmethod
    def store(cls):
        """
        Writes class config to file
        """
        result = True
        content = cls.__fields()
        if not cls._file().exists:
            cls._file().create()
        for new_field in content:
            set_result = cls._file().set(new_field, 
                         Configurable.__encode(content[new_field]))
            result = False if not set_result else result
        return result

    def __inst_store(self):
        """
        Writes instance config to file
        """
        result = True
        content = self.__fields()
        if not self._file().exists:
            self._file().create()
        for new_field in content:
            set_result = self._file().set(new_field, 
                         Configurable.__encode(content[new_field]))
            result = False if not set_result else result
        return result

Configurable类现在由几个子类继承,这些子类可能具有全局配置(类绑定的东西)和用户依赖的配置(实例绑定的东西),如:

class Spam(Configurable):
    EGGS = InstanceConfig('foo')
    GLOBAL_EGGS = ClassConfig('bar')

现在我面临的问题是,每次按顺序在许多实例上执行load()时,InstanceConfigEntry将复制前一个实例中的值:

class RETARD(Daemon):
    """
    Real Estate Translation, Archiving and Redirection Daemon
    """
    __source = None # The source interface instance
    __targets = []  # The target interface instances    
    __locked = False    # System locked state flag
    __start_time = None # Start time of loop
    __sleeping = 0  # Remaining time to sleep
    #===========================================================================
    # Default customer config
    #===========================================================================
    SOURCE = InstanceConfig('')     # Name of the source interface
    TARGETS = InstanceConfig([])    # Names of the target interfaces 
    INTERVAL = InstanceConfig(120.0)  # Loop interval
    DEBUG = InstanceConfig(False) # Print the import config?

    def __init__(self, customer):
        """
        Constructor
        """
        print('SOURCE1: ' + str(self.SOURCE))
        super().__init__(customer)
        print('SOURCE2: ' + str(self.SOURCE))
        self.__load()
        print('SOURCE3: ' + str(self.SOURCE))
        # Disable logger on high level to prevent PyXB 
        # from printing messages to the terminal
        logging.disable(9999)

<SNIP>

当像这样加载时(守护进程包含四个不同的实例):

daemons = []
for customer in customers:
    daemons.append(RETARD(customer))

它将产生此输出:

SOURCE1: 
SOURCE2: IS24
SOURCE3: IS24
SOURCE1: IS24
SOURCE2: is24
SOURCE3: is24
SOURCE1: is24
SOURCE2: infobase
SOURCE3: infobase
SOURCE1: infobase
SOURCE2: infobase
SOURCE3: infobase

我不明白这种行为,因为我没有改变班级&#39;属性,但只是实例的属性。 如何避免将更改后的属性发送到下一个实例的实例?

1 个答案:

答案 0 :(得分:1)

这里的问题,我没有意识到,当时绑定到类的InstanceConfigClassConfig条目,模块被加载。当我在运行时期间从实例中为相应的属性分配其他内容时,它当然只是更改了仍然是类绑定的* Config实例的内容。 我通过默认值来解决这个问题,因为它们不包含在相应的配置文件中,如下所示:

from ..lib.files import ConfigFile
from abc import abstractmethod

__all__ = ['ClassConfig',
           'InstanceConfig',
           'Configurable']

class ConfigEntry():
    """
    A Config entry
    """
    __value = None
    __default = None

    def __init__(self, default=None):
        """
        Initializes the 
        """
        self.__default = default
        self.__value = default

    def __set__(self, __, value):
        """
        Sets the value
        """
        self.__value = value

    @property
    def value(self):
        """
        Returns the value
        """
        return self.__value

    @property
    def default(self):
        """
        Access default value
        """
        return self.__default


class ClassConfig(ConfigEntry):
    """
    A class config entry
    """
    def __get__(self, obj, cls):
        """
        Returns its value, when called by a class, else itself
        """
        if obj == None:
            return self.value
        else:
            return self


class InstanceConfig(ConfigEntry):
    """
    An instance config entry
    """
    def __get__(self, obj, cls):
        """
        Returns its value, when called by an instance, else itself
        """
        if obj != None:
            return self.value
        else:
            return self


class Configurable():
    """
    Configuration file binding
    """
    __SUFFIX = '.conf' 
    __TYPES = {int: 'int',
               float: 'float',
               str: 'str',
               bool: 'bool'}
    __file_ = None
    __lbi = '[' # List begin identifier
    __lei = ']' # List end identifier
    __ls = ','  # List separator
    __ts = '←'  # Type separator
    __loaded = False

    def __init__(self, path, suffix=None):
        """
        Initialize the config file
        """
        # Initializes instance methods
        self.__setinstattr()
        suffix = suffix if suffix != None else self.__SUFFIX
        self.__file_ = ConfigFile(path + suffix)
        self.load()

    def __setinstattr(self):
        """
        Set instance attributes
        """
        self.__fields = self.__inst___fields
        self._file = self.__inst____file
        self._force_load = self.__inst__force_load
        self.load = self.__inst_load
        self.store = self.__inst_store

    @staticmethod        
    def __filter(attrs):
        return [a for a in attrs
                if a == a.upper() 
                and not a.startswith('_')]

    @staticmethod
    def __encode(val):
        """
        Encode a value
        """
        t = type(val)        
        if t == list:
            return Configurable.__lbi + \
                Configurable.__ls.join([Configurable.__encode(i) 
                              for i in val]) \
                    + Configurable.__lei 
        elif val == None:
            return None
        else:
            return Configurable.__ts.join([str(val),
                                           Configurable.__TYPES.get(t, '?')])

    @staticmethod
    def __decode(val):
        """
        Decode a value
        """
        def det_type(token):
            """
            Determine the type of a token
            """
            t = token.strip().split(Configurable.__ts)
            if len(t) == 2:
                raw_val = t[0]
                tpe = t[1]
                if tpe == Configurable.__TYPES[str]:
                    return str(raw_val)
                elif tpe == Configurable.__TYPES[int]:
                    return int(raw_val)
                elif tpe == Configurable.__TYPES[float]:
                    return float(raw_val)
                elif tpe == Configurable.__TYPES[bool]:
                    return True if raw_val.lower() in ['1', 
                                                       'true', 
                                                       't'] else False
                else:
                    try:
                        return int(raw_val)
                    except:
                        try:
                            return float(raw_val)
                        except:
                            return raw_val
            return token

        def str2list(s):
            """
            Try to parse a list from a string
            """
            def getlist(val):
                """
                Get a list from a reversed character list of a string
                """
                result = []
                token = ''
                while val:
                    c = val.pop()
                    if c == Configurable.__lei:
                        token = Configurable.__lei
                        result = [getlist(val)] + result
                    elif c == Configurable.__lbi:
                        if (not Configurable.__lbi in token) and (not Configurable.__lei in token):
                            result = [det_type(token)] + result
                        token = c
                        return result
                    elif c == Configurable.__ls:
                        if (not Configurable.__lbi in token) and (not Configurable.__lei in token):
                            result = [det_type(token)] + result
                        token = ''
                    else:
                        token = c + token
                if token:
                    result = [det_type(token)] + result
                return result
            l = []
            for char in s:
                l.append(char)
            l = getlist(l)
            if len(l) == 0:
                return l
            return l.pop()

        return str2list(val)

    @classmethod
    def __fields(cls):
        """
        Get fields for an instance
        """
        result = {}
        class Subclass(cls):
            def __init__(self):
                pass    
        instance = Subclass()
        attrs = Configurable.__filter(dir(instance))
        for a in attrs:
            aval = getattr(instance, a)
            if isinstance(aval, ClassConfig):
                result[a] = aval
        return result

    def __inst___fields(self):
        """
        Get fields of an instance
        """
        result = {}
        cls = self.__class__
        attrs = Configurable.__filter(dir(cls))
        for a in attrs:
            val = getattr(cls, a)
            if isinstance(val, InstanceConfig):
                result[a] = val
        return result

    @classmethod
    @abstractmethod
    def _file(cls):
        """
        Returns the file
        XXX: Implement by calling 
        super()._file(static_path)
        """
        pass

    @classmethod
    def _file_path(cls, path, suffix=None):
        """
        Returns the file relative to a path
        """
        suffix = suffix if suffix != None else cls.__SUFFIX
        f = ConfigFile(path + suffix)
        f.create()
        return f

    def __inst____file(self):
        """
        Returns the file
        """
        return self.__file_

    @classmethod
    def load(cls):
        """
        Loads the config file content, if not yet done into the class
        """
        if not cls.__loaded:
            return cls._force_load() 
        return True

    def __inst_load(self):
        """
        Loads the config file content, if not yet done into the instance
        """
        if not self.__loaded:
            return self._force_load() 
        return True

    @classmethod
    def _force_load(cls):
        """
        Loads the config file's content to the class
        """
        if cls._file().exists:
            data = cls._file().dict()
        else:
            data = {}
        fields = cls.__fields()
        for field in fields:
            val = data.get(field)
            if val == None:
                val = fields[field].default
            else:
                val = Configurable.__decode(val)
            setattr(cls, field, val)
        cls.__loaded = True
        return True

    def __inst__force_load(self):
        """
        Loads the config file's content to the instance
        """
        if self._file().exists:
            data = self._file().dict()
        else:
            data = {}
        fields =  self.__fields()
        for field in fields:
            val = data.get(field)
            if val == None:
                val = fields[field].default
            else:
                val = Configurable.__decode(val)
            setattr(self, field, val)
        self.__loaded = True
        return True

    @classmethod
    def store(cls):
        """
        Writes class config to file
        """
        result = True
        fields = cls.__fields()
        if not cls._file().exists:
            cls._file().create()
        for field in fields:
            val = fields[field].value
            set_result = cls._file().set(field, 
                         Configurable.__encode(val))
            result = False if not set_result else result
        return result

    def __inst_store(self):
        """
        Writes instance config to file
        """
        result = True
        fields = self.__fields()
        if not self._file().exists:
            self._file().create()
        for field in fields:
            val = fields[field].value
            set_result = self._file().set(field, 
                         Configurable.__encode(val))
            result = False if not set_result else result
        return result