優雅地解決 Python 的日常問題
什麼是優雅的程式碼,今天大鄧與你一起學習優雅的python程式碼。本文內容根據PyCon2018(克利夫蘭) Nina Zakharenko - Elegant Solutions For Everyday Python Problems - PyCon 2018
大會演講視訊整理而來。
python魔術方法-雙下劃線
其實我們平常使用的列表、字串等資料型別的一些方法就用到了莫屬方法,比如
a = 'edf' b = 'ggg' print(a+b)
這個字串拼接操作,在字串類的定義中使用了 __add__
這個魔法。現在我們定義Money類來表示不同的貨幣,並能計算匯率。
class Money: #這裡我們以美元作為計價單位1,方便理解 current_rates = {'$':1, '¥':6} def __init__(self, symbol, amount): self.symbol = symbol self.amount = amount def __str__(self): #用來將例項化顯示出來 return f'{self.symbol}{self.amount}' def convert(self, other): #將rmb轉化為美元計價 new_amount = other.amount/self.current_rates[other.symbol]*self.current_rates[self.symbol] return Money(self.symbol, new_amount) dollar = Money('$', 5)rmb = Money('¥', 5) print(dollar) print(rmb) print(rmb.convert(dollar))
執行結果
$5 ¥5 ¥5.0
現在我們想計算這個人持有的dollar和rmb一共值多少錢,這裡就用到 加法__add__
class Money: current_rates = {'$':1, '¥':6} def __init__(self, symbol, amount): self.symbol = symbol self.amount = amount def __str__(self): return f'{self.symbol}{self.amount}' def convert(self, other): #匯率換算 new_amount = other.amount/self.current_rates[other.symbol]*self.current_rates[self.symbol] return Money(self.symbol, new_amount) def __add__(self, other): #將兩種不同的貨幣進行總價值計算 new_amount = self.amount + self.convert(other).amountreturn Money(self.symbol, new_amount) dollar = Money('$', 5)rmb = Money('¥', 5) print(dollar) print(rmb) print(dollar+rmb) print(rmb+dollar)
執行結果
$5 ¥5 $5.833333333333333 ¥35.0
此外還有 __getitem__ 、__len__
等更多的魔術方法,比如
class SquareShape: def __len__(self): #返回正方向的邊數 return 4 my_square = SquareShape() len(my_square)
執行結果
可迭代類
-
為了建立可迭代的(iterable)資料型別,定義時需要用到
__iter__()
-
__iter__()
必須返回迭代器iterator -
為了讓資料是迭代器iterator,必須使用
__next__()
, 當迭代器中沒有更多的元素可供迭代,此時raiseStopIteration
,iterator不再進行迭代。
比如我們在這裡定義一個可迭代資料型別IterableServer
class IterableServer: services = [{'protocol':'ftp', 'port':21}, {'protocol':'ssh', 'port':22}, {'protocol':'http', 'port':80}] def __init__(self): #初始化伺服器索引位置為第一個 self.current_index = 0 def __iter__(self): #沒有此方法,IterableServer就不能for迴圈迭代 return self def __next__(self): while self.current_index < len(self.services): service = self.services[self.current_index] self.current_index+=1 return service['protocol'], service['port'] raise StopIteration #這是咱們平常使用的for迴圈 servers = IterableServer() print(servers) for s in servers: print(s)
執行結果
<__main__.IterableServer object at 0x1092ece10> ('ftp', 21) ('ssh', 22) ('http', 80)
< main .IterableServer object at 0x1092a5898>說明我們是迭代器物件,可以使用for迴圈,這個有點像列表。每次for迴圈,我們都要iter自己本身。所以比較消耗記憶體空間。
現在我們將IterableServer中的 iter 重新定義,使用yield,讓IterableServer變成生成器,每次迴圈只迭代當前位置的元素,而不是將本身全部迭代。
class IterableServer2: services = [{'protocol':'ftp', 'port':21}, {'protocol':'ssh', 'port':22}, {'protocol':'http', 'port':80}] def __init__(self): #初始化伺服器索引位置為第一個 self.current_index = 0 def __iter__(self): for service in self.services: yield service def __next__(self): while self.current_index < len(self.services): service = self.services[self.current_index] self.current_index+=1 return service['protocol'], service['port'] raise StopIteration #這是咱們平常使用的for迴圈 servers2 = IterableServer2() print(servers2) for s in servers2: print(s)
執行結果
<__main__.IterableServer2 object at 0x1092ecc88> {'protocol': 'ftp', 'port': 21} {'protocol': 'ssh', 'port': 22} {'protocol': 'http', 'port': 80}
檢驗下執行速度(時間)
def s1(): servers = IterableServer() for s in servers: sdef s2(): servers2 = IterableServer2() for s in servers2: s%timeit s1()
1.06 µs ± 48.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit s2()
996 ns ± 39.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
從上面的執行時間看,IterableServer2比IterableServer快,大家可以以此來理解同樣的資料,使用列表與生成器的速度是不同的。
getattr(object, name, default)
舉例這是我們正常的方法呼叫
class Dog: sound = 'Bark' def speak(self): print(self.sound +'!',self.sound+'!') my_dog = Dog() my_dog.speak()
執行結果
Bark! Bark!
使用getattr可以讓我們通過使用字串去呼叫例項中的方法
speak_method = getattr(my_dog, 'speak') speak_method()
執行結果
Bark! Bark!
現在可能覺得區別不大,好像沒必要學getattr。但是假設定義的類中有很多種方法,在某種情況下我們需要輸入一個命令的名字,並執行這個方法,就用到getattr
class Operations: def say_hi(self, name): print('hello, ', name) def say_bye(self, name): print('Goodbye, ', name) def default(self, arg): print('Operations不存在這個方法') operations = Operations() getattr(operations, 'say_hi', operations.default)('David')
執行結果
hello,David
getattr(operations, 'say_hiiii', operations.default)('David')
執行結果
Operations不存在這個方法
裝飾器
裝飾器可以用來讓我們的程式碼更簡潔美觀,我們看一個例子。比如我們要舉行一個會議,只讓授權的人蔘加。
class User: def __init__(self, name, is_authenticated=False): self.name = name self.is_authenticated = is_authenticated def __str__(self): return '<User {}>'.format(self.name) user1 = User('david') user2 = User('smith', True) user3 = User('sam', True) users = [user1, user2, user3] for u in users: if u.is_authenticated == True: print(u,'已授權,可以參加會議')
執行結果
<User smith> 已授權,可以參加會議 <User sam> 已授權,可以參加會議
但是涉及到檢驗某人是否有許可權部分的程式碼不美觀簡潔,
def check(func): def wrapper(user): if not user.is_authenticated: raise Exception('抱歉,{}先生您未註冊會議,無權進入會場'.format(user.name)) return func(user) return wrapper @check def display_authenticated_user(user): print(user.name, '有權進入會場') user1 = User('david') user2 = User('smith', True) user3 = User('sam', True) users = [user2, user3, user1] for u in users: display_authenticated_user(u)
執行結果
smith 有權進入會場 sam 有權進入會場 --------------------------------------------------------------------------- ExceptionTraceback (most recent call last) <ipython-input-106-c6d3c0348559> in <module>() 17 18 for u in users: ---> 19display_authenticated_user(u) <ipython-input-106-c6d3c0348559> in wrapper(user) 2def wrapper(user): 3if not user.is_authenticated: ----> 4raise Exception('抱歉,{}先生您未註冊會議,無權進入會場'.format(user.name)) 5return func(user) 6return wrapper Exception: 抱歉,david先生您未註冊會議,無權進入會場
精選文章
ofollow,noindex">五分鐘帶你瞭解隨機森林
深度學習之 圖解LSTM
PyTorch實戰: 使用卷積神經網路對照片進行分類