我最近阅读了一些有关元类的SO问题,虽然看起来大部分时间都没有必要使用(包括我的问题),但我觉得这个案例很有意思。
在struct的python文档中,这里有一个带有namedtuple的例子:
from collections import namedtuple
Student = namedtuple('Student', 'name serialnum school gradelevel')
Student._make(unpack('<10sHHb', record))
我的问题是:是否可以创建一个我可以用来为我做部分的元类?像我的预期解决方案是:
class Student(object):
__metaclass__ = ???
fields = 'name serialnum school gradelevel'
struct = '<10sHHb'
s = Student(record) # and give same output as the _make() call above
我该怎么做?
答案 0 :(得分:5)
绝对是一个有趣的问题,这是我的解决方案:
import struct
import collections
class MetaStruct(type):
def __new__(cls, clsname, bases, dct):
nt = collections.namedtuple(clsname, dct['fields'])
def new(cls, record):
return super(cls, cls).__new__(cls, *struct.unpack(dct['struct'], record))
dct.update(__new__=new)
return super(MetaStruct, cls).__new__(cls, clsname, (nt,), dct)
class Student(object):
__metaclass__ = MetaStruct
fields = 'name serialnum school gradelevel'
struct = '<10sHHb'
record = 'raymond \x32\x12\x08\x01\x08'
s = Student(record)
我的答案与其他答案之间的显着差异在于,Student
的结尾仍然是一个类,s
是Student
的实例(isinstance(s, Student)
返回真正)。我通过让元类将namedtuple添加为新创建的类的基类来完成此操作,并将对象创建新类(Student.__new__
)委托给基类(namedtuple)。
答案 1 :(得分:2)
是的,有可能:
from collections import namedtuple
from struct import pack, unpack
class MetaStruct(type):
def __new__(meta, name, bases, attrs):
nt = namedtuple(name, attrs.pop('fields'))
struct = attrs.pop('struct')
def factory(record):
return nt._make(unpack(struct, record))
return factory
class Student(object):
__metaclass__ = MetaStruct
fields = 'name serialnum school gradelevel'
struct = '<10sHHb'
record = pack('<10sHHb', 'student123', 123, 456, 12)
s = Student(record)
# => Student(name='student123', serialnum=123, school=456, gradelevel=12)
MetaStruct
元类有点欺骗:它定义了指定的namedtuple
,但它不是实际的类(如预期的元类),而是返回一个生成{{的实例的工厂函数。 1}};仍然,使用此元类定义的“类”应该在实例化时返回namedtuple
而不是完全成熟的类实例,因此这是不可避免的。它可以定义一个实际的子类,并将定义的namedtuple
作为父类,但在您的场景中似乎有点过分。
答案 2 :(得分:0)
lanzz的答案非常简洁;这是使用装饰器的替代方法:
class Dec(object):
def __init__(self, cls, fields=None, struct=None):
print cls,fields,struct
self.cls = cls
self.nt = namedtuple(cls.__name__, fields or cls.fields)
self.struct = struct
def __call__(self, record):
return self.nt._make(unpack(self.struct or self.cls.struct, record))
@Dec
class Student(object):
fields = 'name serialnum school gradelevel'
struct = '<10sHHb'
s = Student('abcdefghla00000')
它与元类相同但使用装饰器类。您还可以使用装饰器方法,并且可以修改以将fields / struct作为装饰器参数。这么多的可能性......
一种方法比另一种更好吗?我不确定,它可能归结为偏好。对于这个任务,我可能更喜欢装饰器,因为你不是真的创建类而是命名工厂,但那只是我。