Python 2.7 Python继承和getattr&__getattribute__

Python 2.7 Python继承和getattr&__getattribute__,python-2.7,inheritance,getattr,getattribute,Python 2.7,Inheritance,Getattr,Getattribute,我整天都在与这个问题斗争,并且做了大量的谷歌搜索。我似乎有遗传问题 我有一个名为BaseClass的类,它为我做一些简单的事情,比如设置日志默认值、保存日志和管理只读属性。在过去,我对这个类没有任何问题,但我可以说我现在只使用python几个月了。此外,我认为有几个重要的注意事项: 过去的所有继承都是单一继承。换句话说,我继承了基类,但是继承基类的类不会被另一个类继承。在今天的问题中,我将基类继承到BaseFormData中,然后将其继承到2013年7月的FormData中,然后将其继承到201

我整天都在与这个问题斗争,并且做了大量的谷歌搜索。我似乎有遗传问题

我有一个名为BaseClass的类,它为我做一些简单的事情,比如设置日志默认值、保存日志和管理只读属性。在过去,我对这个类没有任何问题,但我可以说我现在只使用python几个月了。此外,我认为有几个重要的注意事项:

  • 过去的所有继承都是单一继承。换句话说,我继承了基类,但是继承基类的类不会被另一个类继承。在今天的问题中,我将基类继承到BaseFormData中,然后将其继承到2013年7月的FormData中,然后将其继承到2014年1月的FormData中。很明显,现在有更多的层
  • 在过去继承基类的任何类中,我都没有试图重写
    \uuuuu getattr\uuuuuuu
    \uuuuuu getattribute\uuuuuuu
    。今天我是。基类有一个自己的
    \uuuu getattribute\uuu
    方法来管理只读属性的获取。BaseFormData类有一个
    \uuu getattr\uuu
    方法,因为我知道这是一种提供数据访问的正确方法,而不必显式声明所有属性。正在加载的表单数据中有十几条或更多的数据(取决于版本),因此我显式声明了一些重要的或有别名的属性,而剩下的由
    \uuuu getattr\uuuu
    处理
  • 下面是基类中的
    \uuuuu getattribute\uuuuu

    def __getattribute__(self, attr):
            try:
                # Default behaviour
                return object.__getattribute__(self, attr)
            except:
                try:
                    return self.__READ_ONLY[attr]
                except KeyError:
                    lcAttr = php_basic_str.strtolower(attr)
                    return self._raw[lcAttr]
    
    def __getattribute__(self, attr):
            try:
                # Default behaviour
                return object.__getattribute__(self, attr)
            except:
                return self.__READ_ONLY[attr]
    
    使用
    self.\u raw
    的行之所以存在,完全是因为继承基类的类经常使用
    \u raw
    。基类不需要
    \u raw
    。理想情况下,对
    \u raw
    的引用只会出现在继承类的
    \u getattr\u
    \u getattribute\u
    中。但是,我需要在基类中使用
    \uuu getattribute\uu
    ,以便使只读功能正常工作

    以及BaseFormData中的
    \uuuu getattr\uuuu

    def __getattr__(self, name):
            """
            Return a particular piece of data from the _raw dict
            """
    
            # All the columns are saved in lowercase
            lcName = s.strtolower(name)
    
            try:
                tmp = self._raw[lcName]
            except KeyError:
                try:
                    tmp = super(BaseFormData, self).__getattribute__(name)
                except KeyError:
                    msg = "'{0}' object has no attribute '{1}'. Note that attribute search is case insensitive."
                    raise AttributeError(msg.format(type(self).__name__, name))
    
            if tmp == 'true':
                return True
            elif tmp == 'false':
                return False
            else:
                return tmp
    
    以下是我收到的错误:

    File "D:\python\lib\mybasics\mybasics\cBaseClass.py", line 195, in __getattribute__
        return self.__READ_ONLY[attr]
    
    RuntimeError: maximum recursion depth exceeded while calling a Python object
    
    简短的版本是,我已经为此奋斗了一整天,因为在这里或那里,我得到了不同的反应。大多数情况下,BaseFormData中的
    \uuu getattr\uuu
    从未被调用。其他时候,我得到这个递归错误。其他时候,我可以让它工作,但后来我添加了一些小东西,一切又打破了

    关于继承和调用
    \uu getattribute\uuu
    \uu getattr\uu
    的顺序,我显然缺少一些东西。我今天对代码做了很多调整,如果内存可用,我就不能在BaseFormData和BaseClass中使用
    \uu getattribute\uuuu
    ,但我记不起这个错误了

    我猜递归问题源于
    \uu getattr\uu
    中的这一行:

    tmp = super(BaseFormData, self).__getattribute__(name)
    
    显然,我要做的是首先查看当前类,然后转到基类
    \uuu getattribute\uuu
    ,以检查只读属性

    非常感谢您的帮助

    -----------一些调整的结果-----------

    因此,我将BaseFormClass的
    \uuuuuuGetAttr\uuuuuuuu
    更改为
    \uuuuuuuuGetAttribute\uuuuuuuuu
    ,并且它似乎在基类的
    \uuuuuuGetAttribute\uuuuuuuuuuuuuuuuuuuuuuuu
    之前运行,这是有意义的

    然而,它导致了无限递归,我认为这可能是由于BaseFormClass的
    \uuuu init\uuu
    和BaseFormClass的子类中发生的某些事情的顺序造成的。然后,它似乎是由于uuu READ u创建得太晚了,我也解决了这个问题

    最后,
    \u raw
    在子类中,而不是在基类中,因此我删除了基类的
    \u getattribute\uu
    中对
    \u raw
    的任何引用。我接受了rchang关于将self更改为object的建议,这对BaseClass的
    \uuu getattribute\uuuu
    有所帮助,但似乎在BaseFormData中引起了问题。我已经从解释器获得了“get”部分,代码如下:

    基类:

    def __getattribute__(self, attr):
            try:
                # Default behaviour
                return object.__getattribute__(self, attr)
            except:
                try:
                    return self.__READ_ONLY[attr]
                except KeyError:
                    lcAttr = php_basic_str.strtolower(attr)
                    return self._raw[lcAttr]
    
    def __getattribute__(self, attr):
            try:
                # Default behaviour
                return object.__getattribute__(self, attr)
            except:
                return self.__READ_ONLY[attr]
    
    BaseFormClass:

    def __getattribute__(self, name):
            # All the columns are saved in lowercase
            lcName = s.strtolower(name)
    
            try:
                # Default behaviour
                return object.__getattribute__(self, name)
            except:
                try:
                    tmp = object.__getattribute__(self, '_raw')[lcName]
                except KeyError:
                    try:
                        tmp = BaseClass.__getattribute__(self, name)
                    except KeyError:
                        msg = "'{0}' object has no attribute '{1}'. Note that attribute search is case insensitive."
                        raise AttributeError(msg.format(type(self).__name__, name))
    
                if tmp == 'true':
                    return True
                elif tmp == 'false':
                    return False
                else:
                    return tmp
    
    def __getattribute__(self, name):
            # All the columns are saved in lowercase
            lcName = s.strtolower(name)
    
            try:
                # Default behaviour
                return object.__getattribute__(self, name)
            except:
                name = s.str_replace('_' + type(self).__name__, '', name)
                lcName = s.strtolower(name)
    
                try:
                    tmp = object.__getattribute__(self, '_raw')[lcName]
                except KeyError:
                    #tmp = BaseClass.__getattribute__(self, name)
                    tmp = super(BaseFormData, self).__getattribute__(name)
    
                if tmp == 'true':
                    return True
                elif tmp == 'false':
                    return False
                else:
                    return tmp
    
    讽刺的是,这又产生了另一个问题。当我试图覆盖只读属性时,会触发日志警告,但在调用
    self.\uu instanceId
    时失败。这个日志警告昨天运行得很好(当我可以让类无误地实例化时)。我可以这样实例化这个类:

    a = Jan2014Lead(leadFile)
    
    a.__instanceId
    Out[7]: '8c08dee80ef56b1234fc4822627febfc'
    
    并按如下方式获取实例ID:

    a = Jan2014Lead(leadFile)
    
    a.__instanceId
    Out[7]: '8c08dee80ef56b1234fc4822627febfc'
    
    以下是实际错误:

    File "D:\python\lib\mybasics\mybasics\cBaseClass.py", line 255, in write2log
        logStr = "[" + self.__instanceId + "]::[" + str(msgCode) + "]::" + msgMsg
    
      File "cBaseFormData.py", line 226, in __getattribute__
        raise AttributeError(msg.format(type(self).__name__, name))
    
    AttributeError: 'Jan2014Lead' object has no attribute '_BaseClass__instanceId'. Note that attribute search is case insensitive.
    
    -----------让它工作,但似乎黑客-----------

    因此,上面的错误是在只读中查找BaseClass uuuuu instanceId,但是uuu instanceId在只读中。因此,我简单地修剪了传递的attr字符串,以便从一开始就删除_基类


    这看起来像是一个黑客。有没有一种标准的方法可以做到这一点?

    我认为您遇到的是无限递归,因为
    \uuuuu getattribute\uuuu
    试图直接查找self的属性,因此导致
    \uuu getattribute\uuuuu
    在那些块之外的块中调用自己。见本问题:

    也许可以尝试以下操作(只需使用
    对象将您在try块中使用的相同技术扩展到except块即可)


    我不能说我已经完全解决了这个问题。但是,基类
    \uuu getattirbute\uuu
    方法需要更改调用对象的顺序:

    def __getattribute__(self, attr):
            if attr in self.__READ_ONLY:
                return self.__READ_ONLY[attr]
            else:
                # Default behaviour
                return object.__getattribute__(self, attr)
    
    然后,我需要对BaseFormClass中的
    \uuuu getattribute\uuuu
    进行另一个更改:

    def __getattribute__(self, name):
            # All the columns are saved in lowercase
            lcName = s.strtolower(name)
    
            try:
                # Default behaviour
                return object.__getattribute__(self, name)
            except:
                try:
                    tmp = object.__getattribute__(self, '_raw')[lcName]
                except KeyError:
                    try:
                        tmp = BaseClass.__getattribute__(self, name)
                    except KeyError:
                        msg = "'{0}' object has no attribute '{1}'. Note that attribute search is case insensitive."
                        raise AttributeError(msg.format(type(self).__name__, name))
    
                if tmp == 'true':
                    return True
                elif tmp == 'false':
                    return False
                else:
                    return tmp
    
    def __getattribute__(self, name):
            # All the columns are saved in lowercase
            lcName = s.strtolower(name)
    
            try:
                # Default behaviour
                return object.__getattribute__(self, name)
            except:
                name = s.str_replace('_' + type(self).__name__, '', name)
                lcName = s.strtolower(name)
    
                try:
                    tmp = object.__getattribute__(self, '_raw')[lcName]
                except KeyError:
                    #tmp = BaseClass.__getattribute__(self, name)
                    tmp = super(BaseFormData, self).__getattribute__(name)
    
                if tmp == 'true':
                    return True
                elif tmp == 'false':
                    return False
                else:
                    return tmp
    
    这里实际上只有两个变化。简单的一点是,首先,我删除了raise,让基类处理它。更难描述的更改是我使用
    s.str\u replace
    去掉类名的更改。如果没有这一行,Python通常会寻找