仅静态方法 class 和 Python 中的子 classes - 是否有更好的设计模式?
Static method-only class and subclasses in Python - is there a better design pattern?
我正在为金融工具定价,每个金融工具对象都需要一个日计数器作为 属性。有 4 种日计数器,它们的两种方法 year_fraction
和 day_count
中的每一种都有不同的实现。金融工具上的这个天数计数器 属性 在其他 classes 定价时使用,以了解如何适当地贴现曲线等。但是,所有的天数计算方法都是静态的,无非就是应用一些公式。
因此,尽管我在网上读到的所有内容都告诉我不要使用静态方法,而只使用模块级函数,但我无法找到一种方法来很好地传递正确的 DayCounter 而不是实现这样的东西
class DayCounter:
__metaclass__ = abc.ABCMeta
@abc.abstractstaticmethod
def year_fraction(start_date, end_date):
raise NotImplementedError("DayCounter subclass must define a year_fraction method to be valid.")
@abc.abstractstaticmethod
def day_count(start_date, end_date):
raise NotImplementedError("DayCounter subclass must define a day_count method to be valid.")
class Actual360(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class Actual365(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class Thirty360(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class ActualActual(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
因此,在将工具作为参数传递的某些特定工具的定价引擎中,我可以根据需要使用工具的日计数器 属性。
我是否遗漏了 Python 中更惯用的/在风格上可以接受的东西,或者这似乎适合仅用于静态方法的 classes?
示例:
我有一个 class FxPricingEngine,它有一个 __init__
方法传递给 FxInstrument 和随后的 underlying_instrument
属性。然后,为了使用我的定价引擎的 Value
方法,我需要使用特定日期计数器对曲线进行折扣。我有一个 YieldCurve
class 和一个 discount
方法,我将 self.underlying_instrument.day_counter.year_fraction
传递给该方法,这样我就可以应用正确的公式。实际上,classes 所做的只是为独特的实现提供一些逻辑组织。
坦率地说,以这种方式定义静态方法没有多大意义。在您的情况下,静态方法的唯一目的是为您的函数名称提供命名空间,即您可以像 Actual365.day_count
一样调用您的方法,从而更清楚地表明 day_count 属于 Actual365
功能。
但是您可以通过定义一个名为 actual365
.
的模块来实现同样的想法
import actual365
actual365.day_count()
就面向对象而言,您的代码没有提供 OOP 设计所提供的任何优势。您刚刚将函数包装在 class.
中
现在我注意到你所有的方法都使用 start_date 和 end_date,如何将它们用作实例变量。
class Actual365(object):
def __init__(self, start_date, end_date):
self.start_date, self.end_date = start_date, end_date
def day_count(self):
# your unique formula
...
此外 AbstractClasses
在像 Python 这样的 Duck-Typed 语言中没有多大意义。只要某个对象提供了一种行为,它就不需要从一些抽象 class.
继承
如果这对您不起作用,则仅使用函数可能是更好的方法。
没有
据我所知没有。我认为您的设计模式还可以。特别是如果您的代码有可能增长到每个 class 超过 2 个函数,并且 class 中的函数是连接的。
如果可以使用任何函数组合,请使用函数传递 方法。
modules 方法和您的方法非常相似。模块的优点或缺点(视情况而定)是您的代码被分成许多文件。您的方法允许您使用 isinstance
,但您可能不需要它。
直接传递函数
如果你只有一个函数,你可以直接传递这个函数而不是使用 class。但是一旦你有 2 个或更多函数 和不同的实现,classes 对我来说似乎很好。只需添加一个文档字符串来解释用法。我假设一个 class 中的两个函数实现有些关联。
使用模块
您可以使用模块而不是 classes(例如模块 Actual365DayCounter 和模块 Actual360DayCounter)并使用类似 if something: import Actual360DayCounter as daycounter
和 else: import Actual365ayCounter as daycounter
.
的东西
或者您可以导入所有模块并将它们放入字典中(感谢@freakish 的评论),例如 MODULES = { 'Actual360': Actual360, ... }
并简单地使用 MODULES[my_var]
.
但我怀疑这是否是一种更好的设计模式,因为您会将源代码拆分成许多小模块。
另一个选项:
一种方法是只使用一个 class:
class DayCounter:
def __init__(self, daysOfTheYear = 360):
self.days_of_the_year = daysOfTheYear
并使用 self.days_of_the_year
创建函数。但这仅在 days_of_the_year
实际上是函数的参数时才有效。如果您必须在函数实现中使用大量 if ... elif .. elif ...
,这种方法会更糟
实际上,面向对象在您的场景中没有任何意义:没有与您的类型实例关联的数据,因此某种类型的任何两个对象(例如 Thirty360
)将相等(即你只有单身人士)。
您似乎希望能够根据行为对客户端代码进行参数化 - 您的方法所操作的数据不是在构造函数中给出的,而是通过方法的参数给出的。在那种情况下,简单的自由函数可能是更直接的解决方案。
例如,给定一些在您的柜台上运行的假想客户端代码,例如:
def f(counter):
counter.day_count(a, b)
# ...
counter.year_fraction(x, y)
...你也可以想象立即传递两个函数作为参数而不是一个对象,例如有
def f(day_count, year_fraction):
day_count(a, b)
# ...
year_fraction(x, y)
在调用方,您将传递普通函数,例如
f(thirty360_day_count, thirty360_year_fraction)
如果您愿意,您也可以为函数使用不同的名称,或者您可以在单独的模块中定义它们以获得命名空间。您还可以轻松地传递特殊功能,例如 way(例如,如果您只需要 day_count 是正确的,但 year_fraction 可能是空话)。
我正在为金融工具定价,每个金融工具对象都需要一个日计数器作为 属性。有 4 种日计数器,它们的两种方法 year_fraction
和 day_count
中的每一种都有不同的实现。金融工具上的这个天数计数器 属性 在其他 classes 定价时使用,以了解如何适当地贴现曲线等。但是,所有的天数计算方法都是静态的,无非就是应用一些公式。
因此,尽管我在网上读到的所有内容都告诉我不要使用静态方法,而只使用模块级函数,但我无法找到一种方法来很好地传递正确的 DayCounter 而不是实现这样的东西
class DayCounter:
__metaclass__ = abc.ABCMeta
@abc.abstractstaticmethod
def year_fraction(start_date, end_date):
raise NotImplementedError("DayCounter subclass must define a year_fraction method to be valid.")
@abc.abstractstaticmethod
def day_count(start_date, end_date):
raise NotImplementedError("DayCounter subclass must define a day_count method to be valid.")
class Actual360(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class Actual365(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class Thirty360(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
class ActualActual(DayCounter):
@staticmethod
def day_count(start_date, end_date):
# some unique formula
@staticmethod
def year_fraction(start_date, end_date):
# some unique formula
因此,在将工具作为参数传递的某些特定工具的定价引擎中,我可以根据需要使用工具的日计数器 属性。
我是否遗漏了 Python 中更惯用的/在风格上可以接受的东西,或者这似乎适合仅用于静态方法的 classes?
示例:
我有一个 class FxPricingEngine,它有一个 __init__
方法传递给 FxInstrument 和随后的 underlying_instrument
属性。然后,为了使用我的定价引擎的 Value
方法,我需要使用特定日期计数器对曲线进行折扣。我有一个 YieldCurve
class 和一个 discount
方法,我将 self.underlying_instrument.day_counter.year_fraction
传递给该方法,这样我就可以应用正确的公式。实际上,classes 所做的只是为独特的实现提供一些逻辑组织。
坦率地说,以这种方式定义静态方法没有多大意义。在您的情况下,静态方法的唯一目的是为您的函数名称提供命名空间,即您可以像 Actual365.day_count
一样调用您的方法,从而更清楚地表明 day_count 属于 Actual365
功能。
但是您可以通过定义一个名为 actual365
.
import actual365
actual365.day_count()
就面向对象而言,您的代码没有提供 OOP 设计所提供的任何优势。您刚刚将函数包装在 class.
中现在我注意到你所有的方法都使用 start_date 和 end_date,如何将它们用作实例变量。
class Actual365(object):
def __init__(self, start_date, end_date):
self.start_date, self.end_date = start_date, end_date
def day_count(self):
# your unique formula
...
此外 AbstractClasses
在像 Python 这样的 Duck-Typed 语言中没有多大意义。只要某个对象提供了一种行为,它就不需要从一些抽象 class.
如果这对您不起作用,则仅使用函数可能是更好的方法。
没有
据我所知没有。我认为您的设计模式还可以。特别是如果您的代码有可能增长到每个 class 超过 2 个函数,并且 class 中的函数是连接的。
如果可以使用任何函数组合,请使用函数传递 方法。
modules 方法和您的方法非常相似。模块的优点或缺点(视情况而定)是您的代码被分成许多文件。您的方法允许您使用 isinstance
,但您可能不需要它。
直接传递函数
如果你只有一个函数,你可以直接传递这个函数而不是使用 class。但是一旦你有 2 个或更多函数 和不同的实现,classes 对我来说似乎很好。只需添加一个文档字符串来解释用法。我假设一个 class 中的两个函数实现有些关联。
使用模块
您可以使用模块而不是 classes(例如模块 Actual365DayCounter 和模块 Actual360DayCounter)并使用类似 if something: import Actual360DayCounter as daycounter
和 else: import Actual365ayCounter as daycounter
.
或者您可以导入所有模块并将它们放入字典中(感谢@freakish 的评论),例如 MODULES = { 'Actual360': Actual360, ... }
并简单地使用 MODULES[my_var]
.
但我怀疑这是否是一种更好的设计模式,因为您会将源代码拆分成许多小模块。
另一个选项:
一种方法是只使用一个 class:
class DayCounter:
def __init__(self, daysOfTheYear = 360):
self.days_of_the_year = daysOfTheYear
并使用 self.days_of_the_year
创建函数。但这仅在 days_of_the_year
实际上是函数的参数时才有效。如果您必须在函数实现中使用大量 if ... elif .. elif ...
,这种方法会更糟
实际上,面向对象在您的场景中没有任何意义:没有与您的类型实例关联的数据,因此某种类型的任何两个对象(例如 Thirty360
)将相等(即你只有单身人士)。
您似乎希望能够根据行为对客户端代码进行参数化 - 您的方法所操作的数据不是在构造函数中给出的,而是通过方法的参数给出的。在那种情况下,简单的自由函数可能是更直接的解决方案。
例如,给定一些在您的柜台上运行的假想客户端代码,例如:
def f(counter):
counter.day_count(a, b)
# ...
counter.year_fraction(x, y)
...你也可以想象立即传递两个函数作为参数而不是一个对象,例如有
def f(day_count, year_fraction):
day_count(a, b)
# ...
year_fraction(x, y)
在调用方,您将传递普通函数,例如
f(thirty360_day_count, thirty360_year_fraction)
如果您愿意,您也可以为函数使用不同的名称,或者您可以在单独的模块中定义它们以获得命名空间。您还可以轻松地传递特殊功能,例如 way(例如,如果您只需要 day_count 是正确的,但 year_fraction 可能是空话)。