Pythonメモ : あまり知られていない(かもしれない)テクニック集 その3
- lightweightswitch.py : 辞書をswitch文として使う
- namedformatting.py : 辞書を使った文字列フォーマット
- boolisslow.py : ブール値でのループは遅い(Python 2のみ)
- calculator.py : operatorモジュールを使用した計算例
- setoperators.py : setのoperatorとメソッドを使った集合演算
- codetofunction.py : コードで書いた計算式を関数にする
- nested_functions.py : ネストされた関数
- objgetnamedattribute.py : オブジェクトの属性値を取得
- unique_by_attr.py : 同じ属性値のオブジェクトの重複削除
- common_seq_method.py : シーケンス型で共通のメソッドを実行
- contextmanagers.py : コンテキストマネージャ
- cacheproperty.py : デコレータを使用した値のキャッシュ
- deck_as_list.py : 特殊メソッドとnamedtupleを使用したトランプの例
- socketmsghandling.py : ソケットプログラミング
上記リポジトリにあまり知られていない(かもしれない)Pythonのテクニックが49個まとめられていたので残りを見ていく(その1、その2は下記リンク)。
lightweightswitch.py : 辞書をswitch文として使う
Pythonにはswitch文がないので辞書をswitch文の代わりとして使う方法。
コード
#! /usr/bin/env python3 """lightweight switch statement""" a = { True: 1, False: -1, None: 0 } print(a.get(False, 0)) """works with functions as well""" def add(a, b): return a + b def subtract(a, b): return a - b b = { '+': add, '-': subtract } print(b['+'](1, 1))
実行結果
-1 2
namedformatting.py : 辞書を使った文字列フォーマット
辞書を使った文字列フォーマットの方法。
コード
#! /usr/bin/env python3 """easy string formatting using dicts""" d = {'name': 'Jeff', 'age': 24} print("My name is %(name)s and I'm %(age)i years old." % d) """for .format, use this method""" d = {'name': 'Jeff', 'age': 24} print("My name is {name} and I'm {age} years old.".format(**d)) """alternate .format method""" print("My name is {} and I'm {} years old.".format('Jeff','24')) """dict string formatting""" c = {'email': 'jeff@usr.com', 'phone': '919-123-4567'} print('My name is {0[name]}, my email is {1[email]} and my phone number is {1[phone]}'.format(d, c)) """object string formatting""" class Person: pass me = Person() me.name = 'Jeff' me.email = 'jeff@usr.com' me.phone = '919-123-4567' print('My name is {me.name}, my email is {me.email} and my phone number is {me.phone}'.format(me=me))
実行結果
My name is Jeff and I'm 24 years old. My name is Jeff and I'm 24 years old. My name is Jeff and I'm 24 years old. My name is Jeff, my email is jeff@usr.com and my phone number is 919-123-4567 My name is Jeff, my email is jeff@usr.com and my phone number is 919-123-4567
boolisslow.py : ブール値でのループは遅い(Python 2のみ)
True
とFalse
はPython 3では予約語だが、Python 2では予約語ではないのでループで使用すると遅くなる。実行結果はPython 3では同じだが、Python 2ではブール値の方が遅い(timeit
は実行時間計測モジュール)。なおPython 2ではTrue
とFalse
の値を変えることができる。
コード
""" True and False are keywords in python3, but not in Python2. In Python2 the value of True is 1, and the value of False is 0. True and False can be regarded as vars and thus they are slow in while loop. By the way, True and False can be changed in Python2. Note: This leads to a performance improvement only in Python2. """ from timeit import timeit def test_true(): count = 1000 while True: # here is True if count < 0: break count -= 1 def test_1(): count = 1000 while 1: # here is 1 if count < 0: break count -= 1 # on my macbook pro 15 # test_true is about 59.3047289848 seconds # test_1 is about 39.0966179371 seconds print(timeit(test_true, number=1000000)) print(timeit(test_1, number=1000000)) # True and False can be changed in python2 True = 0 False = 100 print(True) print(False)
実行結果(Python3。True = 0
とFalse = 100
はエラーになるのでコメントアウト)
48.65638999501243 49.07187956303824 True False
実行結果(Python2)
39.3514950275 28.439852953 0 100
calculator.py : operatorモジュールを使用した計算例
operatorモジュールはPythonの組み込み演算子に対応する関数群を提供する(ドキュメント)。例えば、operator.add(x, y)
とx+y
は等価。
コード
#!/usr/bin/env python3 """ This program lets you create a simple command line calculator without using the 'if..else' construct. It uses built-in 'operator' module to accomplish the same Created with help of an answer on stackoverflow. Don't have the exact link. """ import operator ops = { "+": operator.add, "-": operator.sub, "/": operator.truediv, "*": operator.mul } x = input("Enter an operator [OPTIONS: +, -, *, /]: ") y = int(input("Enter number: ")) z = int(input("Enter number: ")) print (ops[x](y, z))
実行結果
Enter an operator [OPTIONS: +, -, *, /]: + Enter number: 1 Enter number: 2 3
setoperators.py : setのoperatorとメソッドを使った集合演算
setのoperatorとメソッドを使った集合演算(ドキュメント)。
コード
#! /usr/bin/env python3 """Python provides usual set operator""" a = set(['a', 'b', 'c', 'd']) b = set(['c', 'd', 'e', 'f']) c = set(['a', 'c']) # Intersection print(a & b) # Subset print(c < a) # Difference print(a - b) # Symmetric Difference print(a ^ b) # Union print(a | b) """using methods instead of operators which take any iterable as a second arg""" a = {'a', 'b', 'c', 'd'} b = {'c', 'd', 'e', 'f'} c = {'a', 'c'} print(a.intersection(["b"])) print(a.difference(["foo"])) print(a.symmetric_difference(["a", "b", "e"])) print(a.issuperset(["b", "c"])) print(a.issubset(["a", "b", "c", "d", "e", "f"])) print(a.isdisjoint(["y", 'z'])) print(a.union(["foo", "bar"])) a.intersection_update(["a", "c", "z"]) print(a)
実行結果
{'d', 'c'} True {'a', 'b'} {'a', 'b', 'f', 'e'} {'d', 'e', 'b', 'a', 'c', 'f'} {'b'} {'b', 'a', 'd', 'c'} {'d', 'c', 'e'} True True True {'d', 'b', 'a', 'c', 'bar', 'foo'} {'a', 'c'}
codetofunction.py : コードで書いた計算式を関数にする
コードで書いた計算式を関数にする方法。
コード
#! /usr/bin/env python3 ''' A simple way to convert arbitrary Python code into a function ''' import math # using sin, cos and sqrt for example ''' Takes a code string and returns a ready-to-use function ''' def compile_(s): code = """def f(x):\n return {}""".format(s) # wrap the string as a function f(x) scope = {"sin": math.sin, "cos": math.cos, "sqrt": math.sqrt} # define the scope for the code to use exec(code, scope) # execute code inside the given scope # f(x) gets defined inside %vis% return scope["f"] # now we only have to extract it and return f = compile_("x**2 + 2*sin(x)") print(f(10))
実行結果
98.91195777822126
nested_functions.py : ネストされた関数
ネストされた関数の例。
コード
#!/usr/bin/env python3 """nested functions""" def addBy(val): def func(inc): return val + inc return func addFive = addBy(5) print(addFive(4)) addThree = addBy(3) print(addThree(7))
実行結果
9 10
objgetnamedattribute.py : オブジェクトの属性値を取得
getattr
でオブジェクトの属性値を取得する方法(ドキュメント)。
コード
#! /usr/bin/env python3 """ Return the value of the named attribute of an object """ class obj(): attr = 1 foo = "attr" print(getattr(obj, foo))
実行結果
1
unique_by_attr.py : 同じ属性値のオブジェクトの重複削除
同じ属性値のオブジェクトの重複を削除する方法。__eq__
などは特殊メソッドのドキュメントを参照。
コード
#! /usr/bin/env python3 """ If we have some sequence of objects and want to remove items with the same attribute value Python creates a dict, where keys are value if attribute (bar in our case), values are object of the sequence. After that the dict is transformed back to list Note: in result we save the last from repeating elements (item2 in our case)! """ class Foo(object): def __init__(self, value): self.bar = value def __eq__(self, other): return self.bar == getattr(other, 'bar') def __hash__(self): return int(self.bar) def __repr__(self): return '{}'.format(self.bar) item1 = Foo(15) item2 = Foo(15) item3 = Foo(5) lst = [item1, item2, item3] print(set(lst)) # original unique_lst = list({getattr(obj, 'bar'): obj for obj in lst}.values()) print(unique_lst) # [item2, item3]
実行結果
{5, 15} [5, 15]
common_seq_method.py : シーケンス型で共通のメソッドを実行
シーケンス型で共通のメソッドを実行する方法。実行結果はlist(map〜
と[f.bar()〜
でそれぞれ5個ずつprintされる。
コード
#! /usr/bin/env python3 """Run common method of big sequence of objects""" import operator class Foo(): def bar(self, *args, **kwargs): print('method bar works') sequence = [Foo() for i in range(5)] # in python3 map returns iterator so we must ask python to process elements by list() # in python2 map(operator.methodcaller('bar'), sequence) works perfectly list(map(operator.methodcaller('bar'), sequence)) # there is another way more understandable [f.bar() for f in sequence]
実行結果
method bar works method bar works method bar works method bar works method bar works method bar works method bar works method bar works method bar works method bar works
contextmanagers.py : コンテキストマネージャ
with
を使うと正常に終わるか例外発生によって終わると、開いたファイルが自動的に閉じられる。これはコンテキストマネージャと呼ばれる機能でcontextlib
を使うとコンテキストマネージャを作成することができる。詳しくは下記ブログを参照。
コード
#! /usr/bin/env python3 """Context managers are useful for automatically releasing resources once you are done with them.""" # common context manager that will close # a file when it has been read with open('README.md') as f: contents = f.read() # make your own context manager import contextlib @contextlib.contextmanager def unlock(resource): resource = "unlocked" try: yield resource finally: resource = "locked" # a resource that is locked resource = "locked" # test that it is indeed locked print(resource) # call your 'unlock' context manager with your resource with unlock(resource) as unlocked: print(unlocked) # check that it is unlocked # ensure it was re-locked when it left the 'unlock' context print(resource)
実行結果
locked unlocked locked
cacheproperty.py : デコレータを使用した値のキャッシュ
デコレータを使用した値のキャッシュの例。1回目はcompute
が出力されるが、2回目以降はresult
のみが出力される。デコレータについては下記リンクを参照。
コード
class PropertyCache: """ a decorator to cache property """ def __init__(self, func): self.func = func def __get__(self, obj, cls): if not obj: return self value = self.func(obj) setattr(obj, self.func.__name__, value) return value class Foo: def __init__(self): self._property_to_be_cached = 'result' @PropertyCache def property_to_be_cached(self): print('compute') return self._property_to_be_cached test = Foo() print(test.property_to_be_cached) print(test.property_to_be_cached)
実行結果
compute result result
deck_as_list.py : 特殊メソッドとnamedtupleを使用したトランプの例
特殊メソッド(ドキュメント)とnamedtuple(ドキュメント)を使用したトランプの例。コメントにあるdunder methodsとはアンダースコア2つの特殊メソッド(__getitem__
など)のことらしい。
コード
#! /usr/bin/env python3 """ How to use dunder methods to add behavior to objects. Here it's an example of how implement a french deck that can be used as a list""" import collections Card = collections.namedtuple('Card', ['rank', 'suit']) class Deck: ranks = [str(n) for n in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split() def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] def __len__(self): return len(self._cards) def __getitem__(self, position): return self._cards[position] card_a = Card('A', 'spades') print(card_a) deck = Deck() len(deck) print(deck[0]) print(deck[-1]) for card in deck: print(card)
実行結果
Card(rank='A', suit='spades') Card(rank='2', suit='spades') Card(rank='A', suit='hearts') # for car in deck: Card(rank='2', suit='spades') Card(rank='3', suit='spades') Card(rank='4', suit='spades') 省略 Card(rank='2', suit='diamonds') 省略 Card(rank='2', suit='clubs') 省略 Card(rank='Q', suit='hearts') Card(rank='K', suit='hearts') Card(rank='A', suit='hearts')
socketmsghandling.py : ソケットプログラミング
ソケットプラグラミングの例(ドキュメント)。functools.partial
(ドキュメント)を使うと簡潔に書ける。
コード
import socket import functools s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn = s.connect(('localhost', 80)) msgs = [] # normal way # while True: # msg = coon.recv(1024) # if recv: # msgs.append(msg) # else: # when no msg come, break # break # hack way with iter and functools.partial # this circle will auto break when msg is empty '' for msg in iter(functools.partial(conn.recv, 1024), b''): msgs.append(msg)