从字符串创建Python对象

2 投票
4 回答
4893 浏览
提问于 2025-04-16 13:22

一些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

这种方法只适用于转换成特定类型,比如floatint,因为转换实际上是在传入的对象中发生的。我想要的是相反的情况,也就是转换发生在接收字符串的对象中。我觉得一个静态的parse方法,正如Paul McGuire所建议的,可能是一个不错的解决办法。

相关问题:

4 个回答

1
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

以及在使用时:

2

这其实是一个根据参数类型不同来处理参数的通用案例。通常情况下,除非真的必要,不建议去检查类型,但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() 或类似的名字。

3

这是一个用字符串来调用构造函数的例子。如果你有一个构造函数需要的不仅仅是字符串的值,那么你可以定义一个类方法工厂方法,这个方法接受一个字符串,从中提取出额外的数据(无论你是怎么编码的),然后用所有必要的参数来调用构造函数。@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")

撰写回答