title: 可愛的豆子——使用Beans思想讓Python代碼更易維護
toc: false
comments: true
date: 2016-06-19 21:43:33
tags: [Python, Java, 經驗]
category: Python
我曾經是一個對Java非常反感的人,因為Java的語法非常啰嗦。而用慣了動態類型的Python再使用靜態類型的Java就會覺得多出了很多的工作量。
因為工作的關系,我開始使用Java來做項目。在這個過程中,我發現Java在某些方面確實和Python不一樣。
有一句話說的好:
語言決定了世界觀。
當我Java用的越來越多的時候,我漸漸發現我不是那么討厭它了。
今天我要講的,是我從Java里面學到的,一個被稱為JavaBeans的東西。
In computing based on the Java Platform, JavaBeans are classes that encapsulate many objects into a single object (the bean). They are serializable, have a zero-argument constructor, and allow access to properties using getter and setter methods.
一句話概括起來: 當一些信息需要使用類似於字典套字典套列表這種很深的結構來儲存的時候,請改用類來儲存。
在Python里面,我以前會寫這樣的代碼:
person_list = [{
'name': 'kingname',
'age': 23,
'sex': 'male'
'detail': {
'address': 'xxx',
'work': 'engineer',
'salary': 100000
}
},
{
'name': 'xiaoming',
'age': 65,
'sex': 'male'
'detail': {
'address': 'yyy',
'work': 'pm',
'salary': 0.5
}
}]
由於Python動態類型的特點,字典里面的value經常是包含了各種類型,有時候,字典里面包含了字典,里面的字典里面還有列表,這個內部字典里面的列表里面又包含了字典……
當我剛剛開始寫Java代碼的時候,也會保留了這個壞習慣,於是我定義的一個變量類似於這樣:
public Map<String, List<Map<String, Map<String, Object>>>> info = .....
並且由於Java是靜態類型語言,有時候Map里面的Value類型還不一致,需要使用Object來代替,等要使用的時候再做類型轉換。
對於這樣的寫法,真可謂是寫代碼一時爽,調試代碼火葬場。我過幾天讀自己的代碼,自己都不知道這個字典里面有哪些內容,也不知道它們有哪些類型,必須到定義的地方去看。
我的Mentor看了我的Java代碼以后,讓我去用一下JavaBeans,於是我的世界瞬間就簡潔多了。后來我將JavaBeans的思想用到Python中,果然Python代碼也變得好看多了。
使用上面person_list這個復雜的結構為例,我用JavaBeans的思想,在Python里面重構它:
class Person(object):
def __init__(self, name='', age=0, sex='', detail=None):
self._name = name
self._age = age
self._sex = sex
self._detail = detail
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
self._name = new_name
@property
def age(self):
return self._age
@age.setter
def age(self, new_age):
self._age = new_age
@property
def sex(self):
return self._sex
@sex.setter
def sex(self, new_sex):
self._sex = new_sex
@property
def detail(self):
return self._detail
@detail.setter
def detail(self, new_detail):
self._detail = new_detail
class Detail(object):
def __init__(self, address='', work='', salary=0):
self._address = address
self._work = work
self._salary = salary
@property
def address(self):
return self._address
@address.setter
def address(self, new_address):
self._address = new_address
@property
def work(self):
return self._work
@work.setter
def work(self, new_work):
self._work = new_work
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, new_salary):
self._salary = new_salary
從這里可以看到,我把字典變成了類。於是,當我想保存我自己的信息和小明的時候,我就可以這樣寫:
detail_kingname = Detail(address='xxx', work='engineer', salary=10000),
kingname = Person(name='kingname', age=23, sex='male', detail=detail_kingname)
detail_xiaoming = Detail(address='yyy', work='pm', salary=0.5),
xiaoming = Person(name='xiaoming', age=65, sex='male', detail=detail_xiaoming)
person_list = [kingname, xiaoming]
這樣寫,雖然說代碼量確實翻了不止一倍,但是當我們后期維護的時候或者遇到問題來調試代碼,我們就能發現這樣寫的好處。
舉一個很簡單的例子,在寫了代碼一年以后,我已經對這段代碼沒有多少印象了,現在我得到了變量person_list
, 我想查看每個人的工資。首先,由於Person
和Detail
這兩個類是已經定義好的,分別放在Person.py
和Detail.py
兩個文件中,於是我點開它們,就知道,原來工資是保存在Detail
這個類中的,關鍵詞是salary
, 而Detail
又是保存在Person
中的,關鍵詞是detail
。
所以要查看每個人的工資,我可以這樣寫:
for person in person_list:
detail = person.detail
salary = detail.salary
print(salary)
但是如果我使用的是最上面字典的那種方式,那么情況就沒有這么簡單了。因為我不知道工資是在這個字典的什么地方。於是我首先要找到person_list
是在哪里初始化的,然后看它里面有什么。在這個例子中,我是一次性把整個列表字典初始化完成的,直接找到列表初始化的地方就知道,原來這個person_list
下面有很多個字典,字典有一個key 叫detail
,這個detail
的value本身又是一個字典,它下面的keysalary
保存了工資的信息。這似乎還比較方便。但是如果字典里面的信息不是一次性初始化完成的呢?萬一detail
這一個key是后面再加的呢?於是又要去找detail
初始化的地方……
第二個好處,使用Beans的時候,每個關鍵字是定義好的,salary
就只能叫做salary
,如果寫成了salarv
, 集成開發環境會立刻告訴你,Detail
沒有salarv
這個屬性。但是如果使用字典的方式,我在給字典賦值的時候,一不小心:
detail['salarv'] = 0.5
由於這里的salarv
是字符串,所以集成開發環境是不會報錯的,只有等你運行的時候,嘗試讀取detail['salary']
里面的值,Python會告訴你:
Traceback (most recent call last):
File "xxx.py", line 1, in <module>
KeyError: 'salary'
總結
將JavaBeans的思想用在Python中,避免字典套字典這種深層嵌套的情況,對於一些需要反復使用的字典,使用類來表示。這樣做,別人讀代碼的時候會更加的容易,自己開發的時候,也會避免出現問題。
本文首發地址: http://kingname.info/2016/06/19/bean-in-python/ 轉載請注明出處。