无故类属性值变化 [英] class attribute changing value for no reason

查看:90
本文介绍了无故类属性值变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序我写一个问题,我不能为我的生命找出我做错了。
好了,所以基本上我编写一个程序来从XML文档中提取数据,并与数据类重presentations操纵。

I have a problem with a program I am writing and i cannot for the life of me figure out what I am doing wrong. Ok so basically I am writing a program to extract data from an XML document and manipulate with class representations of the data.

现在,我在那有一些额外的复杂性在我的计划,我想成为聪明和使用一个描述符(我了解他们,我想我会尝试把它们纳入我的code)

Now, I have some added complexity in my program in that I am trying to be smart and use a descriptor (I am learning about them and thought I would try integrating them into my code)

注:我缩短了问题可以运行一个自包含的python脚本的是:

Note: I shortened the problem to a self contained python script which can be run as is:

#!/usr/bin/env python
import inspect

class Numberise(object):
    def __init__(self, value=0, base=16):
        self.base = base
        self.value = value

    def __get__(self, obj, objtype):
        return self.value
    def __set__(self, obj, val):
        #self.value = self.extract_number(val)
        self.value = val
        print 'set value to:', self.value


class Register(object):
    width = Numberise(base=10)
    def __init__(self, width=16, name='unNamed'):
        super(Register, self).__init__()
        tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthand
        for arg in tuple_args[0]:
            setattr(self, arg, tuple_args[3][arg])

if __name__ == "__main__":

    new_regs = [Register(width=i) for i in range(10)]
    for i,reg in enumerate(new_regs):
        reg.width = i


    for R in new_regs:
        print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)

当我运行该脚本,我得到以下的输出:

When I run the script I get the following output:

C:\Users\gkuhn\Desktop>python test.py
set value to: 0
set value to: 1
set value to: 2
set value to: 3
set value to: 4
set value to: 5
set value to: 6
set value to: 7
set value to: 8
set value to: 9
set value to: 0
set value to: 1
set value to: 2
set value to: 3
set value to: 4
set value to: 5
set value to: 6
set value to: 7
set value to: 8
set value to: 9
In extract(). Id:48851280 name:unNamed, width:9
In extract(). Id:48852080 name:unNamed, width:9
In extract(). Id:48879472 name:unNamed, width:9
In extract(). Id:49285200 name:unNamed, width:9
In extract(). Id:49291504 name:unNamed, width:9
In extract(). Id:49291984 name:unNamed, width:9
In extract(). Id:49292016 name:unNamed, width:9
In extract(). Id:49292048 name:unNamed, width:9
In extract(). Id:49292080 name:unNamed, width:9
In extract(). Id:49292112 name:unNamed, width:9

我想是的宽度值个人把我所造的每个寄存器对象。它看起来是被共享。他们应该不会是个人呢?!

What I'd like is for the width value to be individual to each register object that I have made. It looks like it is being shared. Should they not be individual?!

因此​​,在下面的代码段,我基本上抓住我的新创建的注册对象,并将其添加到已经取得名单。

So in the snippet below, I am basically grabbing my newly created Register objects and adding them to an already made list.

    self.regs = []
    temps = []

    for register in self.ip_root:
        unrolled_regs = UnrollRegister(register)
        new_regs = unrolled_regs.convert()
        for R in new_regs:
            #print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
            if 'extended' in R.name.lower():
                print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
                temps.append(R)
                #print 'In extract(). Id:%s name:%s, width:%d'%(id(R), R.name, R.width)
                a = copy.deepcopy(R)
                #print type(R).__dict__
                #print temps

        #self.regs.extend(new_regs)
        self.regs += new_regs
        #self.regs.extend(unrolled_regs.convert())

    for n in temps:
        print '\tIn loop. Id:%s name:%s, width:%d'%(id(n), n.name, n.width)
        #print type(n).__dict__

请问版画,我一直在试图找出这一点!

Excuse the prints, I have been trying to figure this out!

为类注册的定义是:

class Register(Base):
    width = Numberise(base=10)
    address = Numberise(base=10)
    def __init__(self, name='unNamed', width=16, description='No description provided', 
                 access='RW', address=0, visibility='Public', reset='async',
                 documentation=''):
        super(Register, self).__init__()
        tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthand
        for arg in tuple_args[0]:
            setattr(self, arg, tuple_args[3][arg])

        self.bitfields = []

如前所述,我使用的宽度和地址属性的数据描述符。
为Numberise描述符的定义是:

As mentioned, I am using a data descriptor for the width and address attributes. The definition for the Numberise descriptor is:

class Numberise(Base):
    def __init__(self, value=0, base=16):
        self.base = base
        self.value = self.extract_number(value) 

    def __get__(self, obj, objtype):
        return self.value
    def __set__(self, obj, val):
        self.value = self.extract_number(val)

    def extract_number(self,input):
        "try and get the value being represented"
        if type(input) == int: #its already a number
            return input
        else: #its a string
            RE = re.compile(r"\d?'([hHdDbB])(\w+)") #of the form 'h10 (verilog)
            result = RE.search(input)
            if result is not None:
                radix, string_num = result.groups()
                return int(string_num, {'h':16, 'd':10, 'b':2}[radix.lower()])
            else:
                return int(input, self.base)

基本没有太大包括和我在这里包括它的净度:

Base does not include much and I've included it here for clarity:

class Base(object):
    def __init__(self):
        self._parent_spacer = ''
        self._spacer = '\t'

    @property
    def parent_spacer(self):
        return self._parent_spacer
    @parent_spacer.setter
    def parent_spacer(self, value):
        self._parent_spacer = value
    @property
    def spacer(self):
        return self.parent_spacer+'\t'

此描述背后的想法是,以确保不管我们初始化的宽度和地址属性是,所保存的值将总是整数而不是字符串。

The idea behind this descriptor is to ensure that no matter what we initialise the width and address attributes to be, the saved values will always be integers as opposed to strings.

现在运行code后,所有重要的输出:

Now the all important output after running the code:

In extract(). Id:239825680 name:ASIC_ADC_RESULTS_EXTENDED_READ, width:64
In extract(). Id:239779088 name:ASIC_HART_EXTENDED_RECEIVE_BUFFER, width:64
        In loop. Id:239825680 name:ASIC_ADC_RESULTS_EXTENDED_READ, width:16
        In loop. Id:239779088 name:ASIC_HART_EXTENDED_RECEIVE_BUFFER, width:16

有人能救我的理智,并解释这种行为对我?!

Can someone save my sanity and explain this behavior to me?!

推荐答案

注意:这个答案是类似于OP的答案,但值得一提的有一些区别

阅读文章后的链接从<一个href=\"http://stackoverflow.com/questions/3798835/understanding-get-and-set-and-python-descriptors\">another相关SO质疑,我得出以下code:

After reading the article linked from another relevant SO question, I've come to the following code:

#!/usr/bin/env python
import inspect

class Numberise(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        if self.name not in instance.__dict__:
            raise (AttributeError, self.name)
        return '%o'%(instance.__dict__[self.name])

    def __set__(self, instance, value):
        print ('setting value to: %d'%value)
        instance.__dict__[self.name] = value

class Register(object):
    width = Numberise("truewidth")
    def __init__(self, width=16, name='unNamed'):
        super(Register, self).__init__()
        tuple_args = inspect.getargvalues(inspect.currentframe()) #shorthand
        for arg in tuple_args[0]:
            setattr(self, arg, tuple_args[3][arg])

if __name__ == "__main__":

    new_regs = [Register(width=i) for i in range(10)]
    for i,reg in enumerate(new_regs):
        reg.width = i

    for R in new_regs:
        print ('In extract(). Id:%s name:%s, width:%s, truewidth:%d'%(id(R), R.name, R.width, R.truewidth))

这个程序产生我认为这是什么所需的输出:

This program produces the output which I think is what's desired:

setting value to: 0
setting value to: 1
setting value to: 2
setting value to: 3
setting value to: 4
setting value to: 5
setting value to: 6
setting value to: 7
setting value to: 8
setting value to: 9
setting value to: 0
setting value to: 1
setting value to: 2
setting value to: 3
setting value to: 4
setting value to: 5
setting value to: 6
setting value to: 7
setting value to: 8
setting value to: 9
In extract(). Id:35542384 name:unNamed, width:0, truewidth:0
In extract(). Id:35543152 name:unNamed, width:1, truewidth:1
In extract(). Id:35537776 name:unNamed, width:2, truewidth:2
In extract(). Id:36072560 name:unNamed, width:3, truewidth:3
In extract(). Id:36070384 name:unNamed, width:4, truewidth:4
In extract(). Id:36073040 name:unNamed, width:5, truewidth:5
In extract(). Id:36073072 name:unNamed, width:6, truewidth:6
In extract(). Id:36073104 name:unNamed, width:7, truewidth:7
In extract(). Id:36073136 name:unNamed, width:10, truewidth:8
In extract(). Id:36073168 name:unNamed, width:11, truewidth:9

下面是发生什么解释。在行 WIDTH = Numberise(truewidth)注册类的,我们引进了描述。这是每个类的,而不是每个实例之一,所以没有值存储在Numberise本身:我们到了实际值存储在实例。因为它是定义的描述符允许我们访问注册类的一个实例的成员变量 self.truewidth 。为了说明的目的, __获取__ 方法返回了 truewidth (这是回归例如.__字典__ [self.name] ),但它的字符串重新presentation作为一个八进制数。印刷 R.width 通过描述访问它。印刷 R.truewidth 直接访问它。

Here's an explanation of what happens. In the line width = Numberise("truewidth") of Register class, we introduce the descriptor. It is one per class, not one per instance, so no value is stored in Numberise itself: we got to store the actual values in the instances. The descriptor as it is defined allows us to access the member variable self.truewidth of an instance of Register class. For the purpose of illustration, the __get__ method returns not the truewidth (which would be return instance.__dict__[self.name]), but its string representation as an octal number. Printing R.width is accessing it via descriptor. Printing R.truewidth is accessing it directly.

我们可以调用成员变量宽度,一样的描述,并就没有命名冲突:描述符是类命名空间的一部分,成员变量是每个实例的命名空间的一部分。所以, truewidth 仅用于清楚起见,以更好地辨别两个实体。在现实code,可能将其命名为宽度比较好,所以实际数据隐藏在背后的描述符,你可以不是偶然访问它。

We could have called the member variable width, the same as the descriptor, and there would be no naming conflict: the descriptor is a part of the class namespace, and the member variable is a part of each instance's namespace. So, truewidth is used only for clarity, to better distinguish the two entities. In real code, perhaps naming it width is better, so that the actual data is hidden behind the descriptor, and you can't access it by accident.

此外,该方案提出Python2和Python3既友善,只是通过增加括号的线条与筹集打印

Also, the program was made both Python2 and Python3 friendly, just by adding parentheses to the lines with raise and print.

这篇关于无故类属性值变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆