def get_valid_input(input_string, valid_options):
"""(模块级)验证功能函数:接收一个输入字符串并且列出有效的答案"""
input_string += "({})".format(",".join(valid_options))
response = input(input_string)
while response.lower() not in valid_options: #当输入内容不在有效选项里时
response = input(input_string) #再次输入
return response
class Property:
def __init__(self, square_feet = '', beds = '', baths = '', **kwargs):
super().__init__(**kwargs) #避免在多重继承链中不是最后一个调用
self.square_feet = square_feet
self.num_bedrooms = beds
self.num_baths = baths
def display(self):
print("PROPERTY DETAILS")
print("================")
print("square footage: {}".format(self.square_feet))
print("bedrooms: {}".format(self.num_bedrooms))
print("bathrooms: {}".format(self.num_baths))
print()
def prompt_init():
return dict(square_feet = input("Enter the square feet: "),
beds = input("Enter number of bedrooms: "),
baths = input("Enter number of baths: "))
prompt_init = staticmethod(prompt_init) #设置为静态方法
#静态方法只和一个类(就像类的变量)关联,而不是和一个具体的对象实例。因此它们没有self参数。
#对于静态方法super关键字行不通(没有父类对象,只有父类),所以我们简单地在父类里直接调用这个静态方法。
class Apartment(Property):
#设置有效选项
valid_laundries = ("coin", "ensuite", "none")
valid_balconies = ("yes", "no", "solarium")
def __init__(self, balcony = '', laundry = '', **kwargs):
super().__init__(**kwargs) #调用父类的方法
self.balcony = balcony
self.laundry = laundry
def display(self):
super().display()
print("APARTMENT DETAILS")
print("laundry: %s" % self.laundry)
print("has balcony: %s" % self.balcony)
def prompt_init():
parent_init = Property.prompt_init()
laundry = get_valid_input(
"What laundry facilities does the property have?",
Apartment.valid_laundries)
balcony = get_valid_input(
"Does the property have a balcony?",
Apartment.valid_balconies)
parent_init.update({ #更新原字典
"laundry": laundry,
"balcony": balcony
})
return parent_init
prompt_init = staticmethod(prompt_init)
class House(Property):
#设置有效选项
valid_garage = ("attached", "detached", "none")
valid_fenced = ("yes", "no")
def __init__(self, num_stories = '', garage = '', fenced = '', **kwargs):
super().__init__(**kwargs)
self.garage = garage
self.fenced = fenced
self.num_stories = num_stories
def display(self):
super().display()
print("HOUSE DETAILS")
print("# of stories: {}".format(self.num_stories))
print("garage: {}".format(self.garage))
print("fenced yard: {}".format(self.fenced))
def prompt_init():
parent_init = Property.prompt_init()
fenced = get_valid_input("Is the yard fenced? ", House.valid_fenced)
garage = get_valid_input("Is there a garage? ", House.valid_garage)
num_stories = input("How many stories? ")
parent_init.update({
"fenced": fenced,
"garage": garage,
"num_stories": num_stories
})
return parent_init
prompt_init = staticmethod(prompt_init)
class Purchase:
def __init__(self, price = '', taxes = '', **kwargs):
super().__init__(**kwargs)
#没有超类却仍调用此方法,因为会和其他类结合到一起,并且我们不知道super方法的调用顺序
#当我们在单独的子类里把此类和House类或Apartment类的功能组合时,这个接口非常有帮助
self.price = price
self.taxes = taxes
def display(self):
super().display()
print("PURCHASE DETAILS")
print("selling price: {}".format(self.price))
print("estimated taxes: {}".format(self.taxes))
def prompt_init():
return dict(price = input("What is the selling price?"),
taxes = input("What are the estimated taxes?"))
prompt_init = staticmethod(prompt_init)
class Rental:
def __init__(self, furnished = '', utilities = '', rent = '', **kwargs):
super().__init__(**kwargs)
self.furnished = furnished
self.utilities = utilities
self.rent = rent
def display(self):
super().display()
print("RENTAL DEATAILS")
print("rent: {}".format(self.rent))
print("estimated utilities: {}".format(self.utilities))
print("furnished: {}".format(self.furnished))
def prompt_init():
return dict(rent = input("What is the monthly rent?"),
utilities = input("What are the estimated utilities?"),
furnished = get_valid_input("Is the property furnished?",
("yes", "no")))
prompt_init = staticmethod(prompt_init)
"""以下四个类为组合子类,因为它们的两个父类在其方法里调用了super方法,
所以这几个组合子类不需要__init__方法和display方法。
我们只需扩展这些类,并以正确的顺序调用这些类就行了。(何为正确顺序?见附1)"""
class ApartmentRental(Rental, Apartment):
def prompt_init():
init = Apartment.prompt_init()
init.update(Rental.prompt_init())
return init
prompt_init = staticmethod(prompt_init)
class ApartmentPurchase(Purchase, Apartment):
def prompt_init():
init = Apartment.prompt_init()
init.update(Purchase.prompt_init())
return init
prompt_init = staticmethod(prompt_init)
class HouseRental(Rental, House):
def prompt_init():
init = House.prompt_init()
init.update(Rental.prompt_init())
return init
prompt_init = staticmethod(prompt_init)
class HousePurchase(Purchase, House):
def prompt_init():
init = House.prompt_init()
init.update(Purchase.prompt_init())
return init
prompt_init = staticmethod(prompt_init)
class Agent:
def __init__(self):
self.property_list = []
def display_properties(self):
for property in self.property_list:
property.display()
print("\n")
#字典type_map的键有两个不同字符的元组,它的值是类对象。
type_map = {
("house", "rental"): HouseRental,
("house", "purchase"): HousePurchase,
("apartment", "rental"): ApartmentRental,
("apartment", "purchase"): ApartmentPurchase
}
def add_property(self):
#使用get_valid_input方法来确保我们得到了正确的字典键
property_type = get_valid_input("What type of property?",
("house", "apartment")).lower()
payment_type = get_valid_input("What payment type?",
("purchase", "rental")).lower()
#在字典里查找这个类并且把它存到一个名为PropertyClass的变量里
PropertyClass = self.type_map[(property_type, payment_type)]
#我们并不知道哪个类可用,但是类本身自己知道
#所以可以用多态的方式调用prompt_init方法来得到合适的传递到构造函数字典的值。
init_args = PropertyClass.prompt_init()
#使用关键字参数语法来把字典转换成参数并且构造新的对象来加载正确的数据
self.property_list.append(PropertyClass(**init_args))
附1:
在前面的例子中,继承类的顺序是非常重要的。如果我们写了class HouseRental(House, Rental)而不是class HouseRental(Rental, House)的话,display()方法就不会调用Rental.display()。
在我们这个HouseRental版本中当调用display()方法时,它指向了Rental版本里的方法,这个方法会调用super.display()来得到House版本,House又会调用super.display()来得到正确的版本。
如果我们反过来,显示将会指向House类的display()方法。当调用super方法的时候,它调用了Property父类的方法。但是Property在它的display方法里并没有调用super。这就意味着Rental类的display方法不会被调用。
按照我们的顺序放置继承列表,我们确保了Rental调用super,来处理House这边的继承关系。你可能会想,我们可以给Property.display()方法添加一个super调用,但是这样行不通,因为Property下一个超类是object,object并没有一个display方法。解决这个问题的另一个方法是允许Rental和Purchase扩展Property类而不是直接从object继承,或者动态地修改方法的解析顺序(这里不做介绍)。