Python: **kargs 代替重载?
我遇到了一个关于Python设计的概念性难题。
假设我有一个 City
类,它代表数据库中的一个城市。这个 City
对象可以通过两种方式来初始化:
- 一个整数(实际上是数据库中一个已存在城市的ID)
- 一组属性(
name
、country
、population
等),这将生成一个新的城市并在数据库中获取它的ID。
这意味着 City 对象总是会有一个ID——要么是初始化时提供的ID,要么是从数据库中生成的新ID。
在经典的Java做法中,会使用构造函数重载——一个构造函数接受一个 int
参数,另一个则接受多个类型明确的参数。
我找不到在Python中优雅地实现这个的办法:
- 我可以创建一个基类,里面有一个方法
get_city_id
,然后从这个基类派生出CityFromID
和CityFromNewData
,但这样做需要很多额外的工作,绕过了这个语言的不足。 - 使用类方法似乎也不太合适。
- 使用一个参数列表很长的构造函数也很麻烦:我需要同时传入城市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),它们提供了更好用的方法来做这些事情。