gpt4 book ai didi

python - Python 中的纯静态类 - 使用元类、类装饰器还是其他东西?

转载 作者:太空宇宙 更新时间:2023-11-03 17:30:13 26 4
gpt4 key购买 nike

在我正在开发的程序的一部分中,我想使用数据集X的某些函数项来执行线性回归。所使用的确切模型可由用户配置,特别是要使用哪些术语(或术语集)。这涉及生成矩阵 X',其中 X' 的每一行都是 X 相应行的函数。 X' 的列将是我的回归的预测变量。

例如,假设我的数据集是二维的(X2 列)。如果我们将 xx' 表示为 XX' 的对应行,则假设 x 是二维的 x' 可能类似于

[ 1, x[0], x[1], x[0] * x[1], sqrt(x[0]), sqrt(x[1]), x[0]**2, x[1]**2 ]

您可以看到这些术语成组出现。首先是 1(常数),然后是未变换的数据(线性),然后是两个数据元素的乘积(如果 x 具有两个以上维度,则将是所有成对乘积),然后是平方根以及各项的平方。

我需要在 python 中以某种方式定义所有这些术语集,以便每个术语都有一个用户可读的名称、生成术语的函数、从输入维度获取术语数量的函数、生成标签的函数基于数据列标签的术语等。从概念上讲,这些都感觉应该是 TermSet 类或类似类的实例,但这不太有效,因为它们的方法需要不同。我的第一个想法是采用这样的方法:

termsets = {} # Keep track of sets

class SqrtTerms:
display = 'Square Roots' # user-readable name

@staticmethod
def size(d):
"""Number of terms based on input columns"""
return d

@staticmethod
def make(X):
"""Make the terms from the input data"""
return numpy.sqrt(X)

@staticmethod
def labels(columns):
"""List of term labels based off of data column labels"""
return ['sqrt(%s)' % c for c in columns]

termsets['sqrt'] = SqrtTerms # register class in dict


class PairwiseProductTerms:
display = 'Pairwise Products'

@staticmethod
def size(d):
return (d * (d-1)) / 2

@staticmethod
def make(X):
# Some more complicated code that spans multiple lines
...

@staticmethod
def labels(columns):
# Technically a one-liner but also more complicated
return ['(%s) * (%s)' % (columns[c1], columns[c2])
for c1 in range(len(columns)) for c2 in range(len(columns))
if c2 > c1]

termsets['pairprod'] = PairwiseProductTerms

这有效:我可以从字典中检索类,将我想要使用的类放入列表中,然后对每个类调用适当的方法。尽管如此,创建仅具有静态属性和方法的类看起来很丑陋且不符合Python风格。我想到的另一个想法是创建一个类装饰器,可以像这样使用:

# Convert bound methods to static ones, assign "display" static
# attribute and add to dict with key "name"
@regression_terms(name='sqrt', display='Square Roots')
class SqrtTerms:
def size(d):
return d
def make(X):
return numpy.sqrt(X)
def labels(columns):
return ['sqrt(%s)' % c for c in columns]

这给出了相同的结果,但更干净,并且(对于我自己)更易于阅读和编写(特别是如果我需要很多这样的内容)。然而,事情在幕后实际工作的方式是模糊的,其他读到这篇文章的人可能一开始很难弄清楚到底发生了什么。我还想过为这些创建一个元类,但这听起来有点矫枉过正。我应该在这里使用更好的模式吗?

最佳答案

有些人总是会说这是对语言的滥用。我说 Python 被设计为可滥用的,并且创建不需要解析器但看起来不像 lisp 的 DSL 的能力是它的核心优势之一。

如果您确实有很多这些,请使用元类。如果您这样做,除了拥有术语词典之外,您还可以拥有引用术语的属性。这真的很好,因为你可以有这样的代码:

print Terms.termsets
print Terms.sqrt
print Terms.pairprod
print Terms.pairprod.size(5)

返回结果如下:

{'pairprod': <class '__main__.PairwiseProductTerms'>,
'sqrt': <class '__main__.SqrtTerms'>}
<class '__main__.SqrtTerms'>
<class '__main__.PairwiseProductTerms'>
10

可以做到这一点的完整代码在这里:

from types import FunctionType

class MetaTerms(type):
"""
This metaclass will let us create a Terms class.
Every subclass of the terms class will have its
methods auto-wrapped as static methods, and
will be added to the terms directory.
"""
def __new__(cls, name, bases, attr):
# Auto-wrap all methods as static methods
for key, value in attr.items():
if isinstance(value, FunctionType):
attr[key] = staticmethod(value)
# call types.__new__ to finish the job
return super(MetaTerms, cls).__new__(cls, name, bases, attr)

def __init__(cls, name, bases, attr):
# At __init__ time, the class has already been
# built, so any changes to the bases or attr
# will not be reflected in the cls.
# Call types.__init__ to finish the job
super(MetaTerms, cls).__init__(name, bases, attr)
# Add the class into the termsets.
if name != 'Terms':
cls.termsets[cls.shortname] = cls

def __getattr__(cls, name):
return cls.termsets[name]

class Terms(object):
__metaclass__ = MetaTerms
termsets = {} # Keep track of sets


class SqrtTerms(Terms):
display = 'Square Roots' # user-readable name
shortname = 'sqrt' # Used to find in Terms.termsets

def size(d):
"""Number of terms based on input columns"""
return d

def make(X):
"""Make the terms from the input data"""
return numpy.sqrt(X)

def labels(columns):
"""List of term labels based off of data column labels"""
return ['sqrt(%s)' % c for c in columns]


class PairwiseProductTerms(Terms):
display = 'Pairwise Products'
shortname = 'pairprod'

def size(d):
return (d * (d-1)) / 2

def make(X):
pass

def labels(columns):
# Technically a one-liner but also more complicated
return ['(%s) * (%s)' % (columns[c1], columns[c2])
for c1 in range(len(columns)) for c2 in range(len(columns))
if c2 > c1]

print Terms.termsets
print Terms.sqrt
print Terms.pairprod
print Terms.pairprod.size(5)

如果您将元类和基本Terms 类隐藏在单独的模块中,那么没有人需要查看它——只需from baseterm importTerms 即可。您还可以执行一些很酷的自动发现/自动导入,其中将模块转储到正确的目录中会自动将它们添加到您的 DSL 中。

通过元类,当您发现您希望迷你语言执行的其他操作时,功能集可以轻松地有机增长。

关于python - Python 中的纯静态类 - 使用元类、类装饰器还是其他东西?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31953113/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com