Pythonを何年も使っているうちに、以前は知らなかった機能や関数に出くわしました。その中にはとても便利なのにあまり活用されていないものもあります。このことを念頭に置いて、Pyghonの知っておくべき機能をいくつかまとめてみました。
任意の数の引数を持つ関数
Pythonではオプショナルパラメータを定義できることはすでにご存知でしょう。しかし、関数に任意の数のパラメータを定義する別の方法があります。
まず、オプショナル・パラメーターだけを定義する例を以下に示します。
def function(arg1="",arg2=""):
print "arg1: {0}".format(arg1)
print "arg2: {0}".format(arg2)
function("Hello", "World")
# prints args1: Hello
# prints args2: World
function()
# prints args1:
# prints args2:
それでは、任意の引数を取ることができる関数を定義する方法を見てみましょう。これにはタプルを使います。
def foo(*args): # just use "*" to collect all remaining arguments into a tuple
numargs = len(args)
print "Number of arguments: {0}".format(numargs)
for i, x in enumerate(args):
print "Argument {0} is: {1}".format(i,x)
foo()
# Number of arguments: 0
foo("hello")
# Number of arguments: 1
# Argument 0 is: hello
foo("hello","World","Again")
# Number of arguments: 3
# Argument 0 is: hello
# Argument 1 is: World
# Argument 2 is: Again
Glob() によるファイルの検索
ほとんどのPython関数は長くて説明的な名前を持っています。しかし、glob()名前の関数は、他の場所ですでに馴染みがない限り、何をする関数なのかわからないかもしれません。
listdir()より強力にしたようなものです。パターンマッチを使ってファイルを検索することができます。
import glob
# get all py files
files = glob.glob('*.py')
print files
# Output
# ['arg.py', 'g.py', 'shut.py', 'test.py']
以下のような複数のファイルタイプを探すことができます:
import itertools as it, glob
def multiple_file_types(*patterns):
return it.chain.from_iterable(glob.glob(pattern) for pattern in patterns)
for filename in multiple_file_types("*.txt", "*.py"): # add as many filetype arguements
print filename
# output
#=========#
# test.txt
# arg.py
# g.py
# shut.py
# test.py
各ファイルの絶対パスを取得したい場合は、戻り値に対してrealpath()呼び出します:
import itertools as it, glob, os
def multiple_file_types(*patterns):
return it.chain.from_iterable(glob.glob(pattern) for pattern in patterns)
for filename in multiple_file_types("*.txt", "*.py"): # add as many filetype arguements
realpath = os.path.realpath(filename)
print realpath
# output
#=========#
# C:\xxx\pyfunc\test.txt
# C:\xxx\pyfunc\arg.py
# C:\xxx\pyfunc\g.py
# C:\xxx\pyfunc\shut.py
# C:\xxx\pyfunc\test.py
テスト中のコンポーネントの調整
以下の例では inspect モジュールを使用しています。このモジュールはデバッグにとても便利で、ここで説明したこと以外にもいろいろなことができます。
この記事では、このモジュールのすべての詳細をカバーするわけではありませんが、いくつかの使用例を紹介します。
import logging, inspect
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)-8s %(filename)s:%(lineno)-4d: %(message)s',
datefmt='%m-%d %H:%M',
)
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bow')
def test():
frame,filename,line_number,function_name,lines,index=\
inspect.getouterframes(inspect.currentframe())[1]
print(frame,filename,line_number,function_name,lines,index)
test()
# Should print the following (with current date/time of course)
#10-19 19:57 INFO test.py:9 : Some information
#10-19 19:57 WARNING test.py:10 : A shot across the bow
#(, 'C:/xxx/pyfunc/magic.py', 16, '', ['test()
'], 0)
ユニークIDの生成
一意な文字列を生成する必要がある場合があります。この目的のために md5() 関数を使っている人をよく見かけますが、実はこれはこの目的のためのものではありません。
実際には、uuid()と呼ばれるPython関数があり、この目的で使用されます。
import uuid
result = uuid.uuid1()
print result
# output => various attempts
# 9e177ec0-65b6-11e3-b2d0-e4d53dfcf61b
# be57b880-65b6-11e3-a04d-e4d53dfcf61b
# c3b2b90f-65b6-11e3-8c86-e4d53dfcf61b
文字列は一意であるにもかかわらず、その後ろの数文字が似ていることにお気づきでしょう。これは、生成された文字列がコンピュータのMACアドレスにリンクされているためです。
重複を減らすために、両方の機能を使用することができます。
import hmac,hashlib
key='1'
data='a'
print hmac.new(key, data, hashlib.sha256).hexdigest()
m = hashlib.sha1()
m.update("The quick brown fox jumps over the lazy dog")
print m.hexdigest()
# c6e693d0b35805080632bc2469e1154a8d1072a86557778c27a01329630f8917
# 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
シリアライズ
複雑な変数をデータベースやテキストファイルに格納する必要が生じたことはありませんか?配列やオブジェクトグリッドをフォーマットされた文字列に変換するための派手な方法を思いつく必要はありません。
import pickle
variable = ['hello', 42, [1,'two'],'apple']
# serialize content
file = open('serial.txt','w')
serialized_obj = pickle.dumps(variable)
file.write(serialized_obj)
file.close()
# unserialize to produce original content
target = open('serial.txt','r')
myObj = pickle.load(target)
print serialized_obj
print myObj
#output
# (lp0
# S'hello'
# p1
# aI42
# a(lp2
# I1
# aS'two'
# p3
# aaS'apple'
# p4
# a.
# ['hello', 42, [1, 'two'], 'apple']
これは Python ネイティブのシリアライズ方法です。しかし、近年JSONが普及し、PythonはJSONのサポートを追加しました。JSONを使ってエンコードやデコードができるようになりました。
import json
variable = ['hello', 42, [1,'two'],'apple']
print "Original {0} - {1}".format(variable,type(variable))
# encoding
encode = json.dumps(variable)
print "Encoded {0} - {1}".format(encode,type(encode))
#deccoding
decoded = json.loads(encode)
print "Decoded {0} - {1}".format(decoded,type(decoded))
# output
# Original ['hello', 42, [1, 'two'], 'apple'] - <type 'list'="">
# Encoded ["hello", 42, [1, "two"], "apple"] - <type 'str'="">
# Decoded [u'hello', 42, [1, u'two'], u'apple'] - <type 'list'="">
これはよりコンパクトで、最も重要なことは、JavaScriptや他の多くの言語と互換性があるということです。しかし、複雑なオブジェクトの場合、この情報の一部が失われる可能性があります。
圧縮文字
圧縮というと、普通はZIP構造のようなファイルを思い浮かべます。Pythonでは、アーカイブファイルを介さずに長い文字を圧縮することが可能です。
import zlib
string = """ Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Nunc ut elit id mi ultricies
adipiscing. Nulla facilisi. Praesent pulvinar,
sapien vel feugiat vestibulum, nulla dui pretium orci,
non ultricies elit lacus quis ante. Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Aliquam
pretium ullamcorper urna quis iaculis. Etiam ac massa
sed turpis tempor luctus. Curabitur sed nibh eu elit
mollis congue. Praesent ipsum diam, consectetur vitae
ornare a, aliquam a nunc. In id magna pellentesque
tellus posuere adipiscing. Sed non mi metus, at lacinia
augue. Sed magna nisi, ornare in mollis in, mollis
sed nunc. Etiam at justo in leo congue mollis.
Nullam in neque eget metus hendrerit scelerisque
eu non enim. Ut malesuada lacus eu nulla bibendum
id euismod urna sodales. """
print "Original Size: {0}".format(len(string))
compressed = zlib.compress(string)
print "Compressed Size: {0}".format(len(compressed))
decompressed = zlib.decompress(compressed)
print "Decompressed Size: {0}".format(len(decompressed))
# output
# Original Size: 1022
# Compressed Size: 423
# Decompressed Size: 1022
シャットダウン機能の登録
atexitと呼ばれるモジュールがあり、スクリプトの実行が終了した直後にコードを実行することができます。
スクリプトの実行終了時に、実行時間などのベースラインデータを測定したいとします:
import atexit
import time
import math
def microtime(get_as_float = False) :
if get_as_float:
return time.time()
else:
return '%f %d' % math.modf(time.time())
start_time = microtime(False)
atexit.register(start_time)
def shutdown():
global start_time
print "Execution took: {0} seconds".format(start_time)
atexit.register(shutdown)
# Execution took: 0.297000 1387135607 seconds
# Error in atexit._run_exitfuncs:
# Traceback (most recent call last):
# File "C:\Python27\lib\atexit.py", line 24, in _run_exitfuncs
# func(*targs, **kargs)
# TypeError: 'str' object is not callable
# Error in sys.exitfunc:
# Traceback (most recent call last):
# File "C:\Python27\lib\atexit.py", line 24, in _run_exitfuncs
# func(*targs, **kargs)
# TypeError: 'str' object is not callable
一見とても簡単です。スクリプトの一番下にコードを追加するだけで、スクリプトが終了する前に実行されます。しかし、スクリプトに致命的なエラーがあったり、ユーザーによってスクリプトが終了させられたりすると、実行されないことがあります。
atexit.register()を使用すると、スクリプトが何のために実行を停止したかに関係なく、すべてのコードが実行されます。
はんけつをくだす




