不那么笨重的生成属性的方法?

2024-06-28 20:11:38 发布

您现在位置:Python中文网/ 问答频道 /正文

TLDR公司:

构建具有泛型属性属性的类以及在构建这些属性时可以指定getter和setter的最佳方法是什么?你知道吗

背景:

我正在尝试用Python实现SCPI命令结构的面向对象表示。解析之后,命令层是(子)对象,设置/获取最低的命令对象实际上将与设备通信以设置/获取其参数。你知道吗

我遇到的问题是将通用getter/setter函数赋值给command对象,就像属性一样。我和__setattr__/__getattr__相处得很艰难,因为我总是遇到递归问题。你知道吗

如果在类构建期间和实例化/运行时版本中对属性的设置/获取有区别的话,那就太酷了。或者更好:是否有可能使属性成为属性“对象”?比如:

foo = property()
foo.__getter__ = lambda paramName : devcontroller.request(paramName)
foo.__setter__ = lambda paramName, paramValue : devcontroller.demand(paramName, paramValue)
setattr(self, paramName, foo)

但显然这是不可能的(只读)。你知道吗

目前最好的解决方案是下面的一个,但感觉非常笨拙。例如,无法完成命令对象。你知道吗

class SCPITree(object):
    def __init__(self, cmd, branch):
        self.__dict__['building'] = True
        self.__dict__['params'] = {}
        # parse tree
        for k, v in branch.items():
            catCmd = '{}:{}'.format(cmd, k[1])
            param = k[0]
            if isinstance(v, dict):
                # still a dictionary, so unpack
                #setattr(self, param, SCPITree(catCmd, v))
                self.__dict__[param] = SCPITree(catCmd, v)
            else:
                # have the value type, so this is a leaf
                print('{} of {}'.format(catCmd, v))
                self.__dict__['params'][param] = {'cmd': catCmd, 'type': v}
                #setattr(self, param, v())

        del self.__dict__['building']

    def __getattr__(self, name):
        '''Requests the device for parameter'''
        param = self.__dict__['params'].get(name)
        if param:
            request = param['cmd']+'?'
            print('Requesting device with: {}'.format(request))
            # do actual device communication here
            return 'Response from device, converted to {}'.format(param['type'])
        else:
            if 'building' not in self.__dict__:
                raise KeyError('Device has no SCPI command for {}'.format(name))

    def __setattr__(self, name, value):
        '''Sets a device parameter'''
        param = self.__dict__['params'].get(name)
        if param:
            demand = param['cmd']+' {}'.format(value)
            print('Demanding device with: {}'.format(demand))
            # do actual device communication here
        else:
            if 'building' not in self.__dict__:
                raise KeyError('Device has no SCPI command for {}'.format(name))

if __name__ == '__main__':
    # test SCPI tree parsing
    trunk = {
        ('input', 'INP'):{
            ('agc', 'AGC'):{
                ('mode', 'MOD'):str, 
                ('refLevelDB', 'REF'):float}, 
            ('delayMS', 'DEL'):float, 
            ('freqMHz', 'FREQ'):float}}

    scpi = SCPITree('DCS', trunk)

在运行代码之后,交互是这样的:

DCS:INP:AGC:MOD of <type 'str'>
DCS:INP:AGC:REF of <type 'float'>
DCS:INP:DEL of <type 'float'>
DCS:INP:FREQ of <type 'float'>

In [1]: scpi.input.delayMS
Requesting device with: DCS:INP:DEL?
Out[1]: "Response from device, converted to <type 'float'>"

In [2]: scpi.input.delayMS = 3
Demanding device with: DCS:INP:DEL 3

In [3]: scpi.input.params
Out[3]:
{'delayMS': {'cmd': 'DCS:INP:DEL', 'type': float},
 'freqMHz': {'cmd': 'DCS:INP:FREQ', 'type': float}}

Tags: 对象nameselfcmdformatif属性param