从字符串创建Python对象
一些Python类,比如float,可以把字符串转换成对象:
number_string = "4.5"
assert float(number_string) == 4.5
这从技术上来说是什么呢?是用字符串来调用构造函数吗?但是如果构造函数还需要其他参数来创建对象(不是从字符串来的)呢?
怎么实现一个可以解析字符串来创建实例的类呢?
补充:
看起来float(str)是在调用传入字符串的__float__特殊方法——这个字符串知道它的float值。
每个实现了__float__的方法的对象都可以传给float(obj):
>>> class MyClass:
... def __float__(self):
... return 0.01
...
>>> m = MyClass()
>>> float(m)
0.01
这种方法只适用于转换成特定类型,比如float和int,因为转换实际上是在传入的对象中发生的。我想要的是相反的情况,也就是转换发生在接收字符串的对象中。我觉得一个静态的parse方法,正如Paul McGuire所建议的,可能是一个不错的解决办法。
相关问题:
4 个回答
class MyObject(object):
def __init__(self, data):
super(MyObject,self).__init__()
if isinstance(data, self.__class__): # copy constructor
self.value = data.value
elif isinstance(data, basestring):
self.value = float(data) # parse string
else:
self.value = data
a = MyObject(3.9) # a.value = 3.9
b = MyObject("3.9") # string constructor - b.value = 3.9
c = MyObject(a) # copy constructor - c.value = 3.9
以及在使用时:
这其实是一个根据参数类型不同来处理参数的通用案例。通常情况下,除非真的必要,不建议去检查类型,但Python提供了 type() 和 isinstance() 这两个函数,正是为了这个目的!
你会发现,dict() 这个构造函数可以接受一个已经存在的字典对象、一个包含键值对元组的列表(或者其他可迭代对象),或者关键字参数(这些参数会以字典的形式传入构造函数,但和第一种方式的参数不同)。在Java或者其他静态类型语言中,这些都会有不同的构造方法。但在Python中,任何类型都可以作为任何参数传入。实际上只有一个构造函数(或者说初始化器)。
因此,dict 类型的 __init__() 方法必须聪明地处理第一参数是字典或列表的情况,同时也要处理可选的关键字参数。这两者都必须是可选的。它的实现方式大概是这样的:
class dict(object):
def __init__(self, d={}, **kwd):
if isinstance(d, type(self)):
self.update(d)
else:
for i in d:
self[i[0]] = i[1]
self.update(kwd)
(使用自身类型的对象来定义一个类是有问题的,所以我写的代码可能实际上无法运行,而且 dict 实际上是用C语言实现的,但希望你能理解这个思路。)
在你自己的对象中,你可以让某些参数变成可选的,并根据需要测试参数的类型,以便根据传入的内容进行不同的处理。如果你想处理字符串,可以在Python 2.x中使用 isinstance(arg, basestring)(这样可以同时匹配普通字符串和Unicode字符串),在Python 3中则可以直接使用 isinstance(arg, str)。
保罗建议的工厂类方法也是个不错的主意,特别是对于那些可以用多种方式进行初始化的类。我会把这样的函数命名为 from_str() 或类似的名字。
这是一个用字符串来调用构造函数的例子。如果你有一个构造函数需要的不仅仅是字符串的值,那么你可以定义一个类方法工厂方法,这个方法接受一个字符串,从中提取出额外的数据(无论你是怎么编码的),然后用所有必要的参数来调用构造函数。@tiagoboldt 提到了这个类的定义:
class Student(object):
def __init__ (self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
我会添加这个方法来接受格式为 "名字/年龄/性别" 的字符串:
@classmethod
def parse(cls, s):
# some input validation here would be a good idea
name,age,gender = s.split('/')
age = int(age)
return cls(name, age, gender)
s1 = Student("Bob", 10, "M")
s2 = Student.parse("Bill/12/M")