Python最佳实践:如何处理可以是2种类型的函数参数



我正在构建一个Python类,它可以与PySpark数据帧或Hive表一起使用。输入数据可以是表名称的str,也可以是DataFrame。Python的最佳实践是什么?我想这样做,以便它根据用例的不同而灵活。唯一的区别是数据应该如何传递到类(作为表或Spark DataFrame),其他一切都是一样的。

参见以下示例:

class DataPipeline:
def __init__(self, data):
if isinstance(data, str):
self.df = spark.read.table(data)
elif isinstance(data, DataFrame):
self.df = data
else:
raise ValueError("some error")
def process_data(self):
# do something with the self.df here

不要。使__init__期望DataFrame,并使调用者负责(可能通过类方法)在调用DataPipeline.__init__之前将字符串转换为DataFrame

class DataPipeline:
def __init__(self, data: DataFrame):
self.df = df

@classmethod
def from_string(cls, data: str):
return cls(spark.read.table(data))

p1 = DataPipeline(some_data_frame)
p2 = DataPipeline.from_string("...")  # DataPipline(spark.read.table("..."))

Ryan Singer(据推测,尽管经常被引用,但我还没有找到原始来源)曾说过

软件中如此多的复杂性来自于试图让一件事做两件事。

这里,您试图将一件事(DataPipeline.__init__)变成两件事(使用字符串初始化DataPipeline,使用DataFrame初始化DataPipeline)。

对于重构,我选择对__init__进行最简单的处理(接受DataFrame),将首先将字符串解析为DataFrame的更复杂逻辑转移到一个单独的类方法中,该方法使用适当的数据帧调用__init__


(是否要对类型验证进行任何显式运行时检查以提前失败,或者只是假设调用者将接受未能传递正确类型的值的后果,取决于您。)

您有正确的基本想法,但语法错误——使用raise引发异常(我建议TypeError用于无效类型,而不是ValueError),并确保您设置的是self.df而不是本地vardf:

class DataPipeline:
def __init__(self, data):
if isinstance(data, str):
self.df = spark.read.table(data)
elif isinstance(data, DataFrame):
self.df = data
else:
raise TypeError("Data must be str or DataFrame")

最新更新