通过字典调用classmethods

时间:2013-12-31 01:49:16

标签: python class methods dictionary

我正在研究一个描述一个可以用几个“单位”表达的对象的类,我会说,为了简单起见。让我们说我们谈的是长度。 (它实际上更复杂。)我想要的是用户能够输入1和“英寸”,例如,并自动获取英尺,米,弗隆的成员变量,你也有什么。我希望用户能够输入我正在处理的任何单位,并在所有其他单位中获取成员变量。我的想法是做这样的事情:

class length:
  @classmethod
  def inch_to_foot(cls,inch):
  # etc.
  @classmethod
  def inch_to_meter(cls,inch):
  # etc.

我想你明白了。然后我会在课堂上定义一个字典:

  from_to={'inch':{'foot':inch_to_foot,'meter':inch_to_meter, ...},
           'furlong':{'foot':furlong_to_foot, ...},
           #etc
           }

那么我想我可以写一个__init__方法

   def __init__(self,num,unit):
       cls = self.__class__
       setattr(self,unit,num)
       for k in cls.from_to[unit].keys:
           setattr(self,k,cls.from_to[unit][k](num)

但是没有去。我得到错误“类方法不可调用”。我有什么想法可以使这项工作?有什么想法废弃整个事物并尝试不同的方法?感谢。

3 个答案:

答案 0 :(得分:1)

如果您将from_to变量移至__init__并将其修改为:

cls.from_to={'inch':{'foot':cls.inch_to_foot,'meter':cls.inch_to_meter, }}

然后我认为它可以按预期工作。

不幸的是我无法回答为什么,因为我自己并没有多使用类方法,但我认为它与绑定与未绑定方法有关。无论如何,如果你打印代码中to_from存储的函数与修改后的函数,你会看到它们是不同的(我的是绑定的,你的是类方法对象)

希望有所帮助!

编辑:我已经考虑了一下,我认为问题是因为你在绑定到类之前存储对函数的引用(这并不奇怪的是绑定一旦解析了类的其余部分就会发生)。我的建议是忘记存储函数引用的字典,但要存储(在您选择的某种表示中)字符串,这些字符串表示您可以在它们之间进行更改的单位。例如,您可以选择类似的格式,例如:

from_to = {'inch':['foot','meter']}

然后使用__init__getattr期间查找功能 E.G:

class length:    
    from_to = {'inch':['foot','meter']}

    def __init__(self,num,unit):
        if unit not in self.from_to:
            raise RuntimeError('unit %s not supported'%unit)

        cls = self.__class__
        setattr(self,unit,num)

        for k in cls.from_to[unit]:
            f = getattr(cls,'%s_to_%s'%(unit,k))
            setattr(self,k,f(num))            

    @classmethod
    def inch_to_foot(cls,inch):
        return inch/12.0

    @classmethod
    def inch_to_meter(cls,inch):
        return inch*2.54/100    

a = length(3,'inches')
print a.meter
print a.foot
print length.inch_to_foot(3)

答案 1 :(得分:1)

我不认为使用__init__()方法做一个好主意。我曾经在Guido van Rossum的经典文件Overriding the __new__ methodUnifying types and classes in Python 2.2部分看到了一种有趣的方式。

以下是一些例子:

class inch_to_foot(float):
    "Convert from inch to feet"
    def __new__(cls, arg=0.0):
        return float.__new__(cls, float(arg)/12)

class inch_to_meter(float):
    "Convert from inch to meter"
    def __new__(cls, arg=0.0):
        return float.__new__(cls, arg*0.0254)

print inch_to_meter(5)  # 0.127

答案 2 :(得分:1)

这是一个完全不同的答案,它使用元类并要求转换函数为staticmethods而不是classmethods - 它根据目标单位的名称变为属性。如果搜索任何转换的名称本身,则无需手动定义from_to类型表。

这种方法的一个方面是甚至不调用转换函数,除非对与它们相关联的单元进行间接引用。另一个是它们是动态的,因为返回的结果将反映实例的当前值(与three_pineapples'length类的实例不同,后者存储在实例的数值上调用它们的结果它最初是构建的。)

您从未说过您正在使用的Python版本,因此以下代码适用于Python 2.2 - 2.x。

import re

class MetaUnit(type):
    def __new__(metaclass, classname, bases, classdict):
        cls = type.__new__(metaclass, classname, bases, classdict)

        # add a constructor
        setattr(cls, '__init__',
                lambda self, value=0: setattr(self, '_value', value))

        # add a property for getting and setting the underlying value
        setattr(cls, 'value',
                property(lambda self: self._value,
                         lambda self, value: setattr(self, '_value', value)))

        # add an identity property the just returns the value unchanged
        unitname = classname.lower()  # lowercase classname becomes name of unit
        setattr(cls, unitname, property(lambda self: self._value))

        # find conversion methods and create properties that use them
        matcher = re.compile(unitname + r'''_to_(?P<target_unitname>\w+)''')
        for name in cls.__dict__.keys():
            match = matcher.match(name)
            if match:
                target_unitname = match.group('target_unitname').lower()
                fget = (lambda self, conversion_method=getattr(cls, name):
                            conversion_method(self._value))
                setattr(cls, target_unitname, property(fget))

        return cls

样本用法:

scalar_conversion_staticmethod = (
    lambda scale_factor: staticmethod(lambda value: value * scale_factor))

class Inch(object):
    __metaclass__ = MetaUnit
    inch_to_foot = scalar_conversion_staticmethod(1./12.)
    inch_to_meter = scalar_conversion_staticmethod(0.0254)

a = Inch(3)
print a.inch   # 3
print a.meter  # 0.0762
print a.foot   # 0.25
a.value = 6
print a.inch   # 6
print a.meter  # 0.1524
print a.foot   # 0.5