Python: **kargs 代替重载?

5 投票
3 回答
1620 浏览
提问于 2025-04-16 00:50

我遇到了一个关于Python设计的概念性难题。

假设我有一个 City 类,它代表数据库中的一个城市。这个 City 对象可以通过两种方式来初始化:

  1. 一个整数(实际上是数据库中一个已存在城市的ID)
  2. 一组属性(namecountrypopulation 等),这将生成一个新的城市并在数据库中获取它的ID。

这意味着 City 对象总是会有一个ID——要么是初始化时提供的ID,要么是从数据库中生成的新ID。

在经典的Java做法中,会使用构造函数重载——一个构造函数接受一个 int 参数,另一个则接受多个类型明确的参数。

我找不到在Python中优雅地实现这个的办法:

  • 我可以创建一个基类,里面有一个方法 get_city_id,然后从这个基类派生出 CityFromIDCityFromNewData,但这样做需要很多额外的工作,绕过了这个语言的不足。
  • 使用类方法似乎也不太合适
  • 使用一个参数列表很长的构造函数也很麻烦:我需要同时传入城市ID和其他选项,并在方法内部验证只有特定的参数有值。

使用 **kargs 看起来也不太优雅,因为构造函数的签名并没有清楚地说明需要哪些输入参数,而文档字符串又不够详细:

class City(object):
    def __init__(self, city_id=None, *args, **kargs):
        try:
            if city_id==None:
                self.city_id=city_id
            else:
                self.city_name=kargs['name']
        except:
            error="A city object must be instanciated with a city id or with"+\
            " full city details."
            raise NameError(error)

有没有一种Pythonic且优雅的解决方案来实现构造函数重载?

Adam

3 个回答

2

我觉得类(工厂)方法是最好的,因为这个方法的名字已经很清楚地说明了它的作用。两个独立的函数也可以用:

def load_existing_city(id):
    ...
def create_new_city(name, population, ...):
    ...
4

有一种设计模式叫做 数据访问对象,通常在你的情况中会用到。根据这个模式,你应该把获取和创建数据对象的功能分开到两个类中,一个叫做 City,另一个叫做 CityDAO:

class City:

    def __init__(self, name, country):
        self.name = name
        self.country = country 


class CityDAO:

    def fetch(self, id):
        return query(...)

    def insert(self, city):
        query(...)
7

这样怎么样:

class City(object):
   def __init__(self, name, description, country, populations):
      self.city_name = name
      # etc.

   @classmethod
   def from_id(cls, city_id):
       # initialise from DB 

然后你可以正常创建对象:

 >>> c = City('Hollowberg', '', 'Densin', 3)
 >>> c.id
 1233L

 >>> c2 = City.from_id(1233)

~~~~~~

另外,你可能还想看看 SQLAlchemy (还有 Elixir),它们提供了更好用的方法来做这些事情。

撰写回答