我正在研究一个描述一个可以用几个“单位”表达的对象的类,我会说,为了简单起见。让我们说我们谈的是长度。 (它实际上更复杂。)我想要的是用户能够输入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)
但是没有去。我得到错误“类方法不可调用”。我有什么想法可以使这项工作?有什么想法废弃整个事物并尝试不同的方法?感谢。
答案 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__ method的Unifying 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