LoginSignup
11
16

More than 3 years have passed since last update.

Pythonの組み込み関数69個を制覇する 第3回 e~g

Last updated at Posted at 2019-02-04

Pythonでは、非常に多くの組み込み関数(Built-in Functions)が用意されており、ライブラリをimportせずとも様々な処理を行うことができます。
基本に立ち返って、組み込み関数の使い方を整理して行きたいと思います。
尚、実行例はWindows Subsystem for LinuxのUbuntu18.04にインストールしたPython3.7.1で作成しています。

Pythonの組み込み関数69個を制覇する 第1回 a~b
Pythonの組み込み関数69個を制覇する 第2回 c~d
Pythonの組み込み関数69個を制覇する 第4回 h~i
Pythonの組み込み関数69個を制覇する 第5回 l~o
Pythonの組み込み関数69個を制覇する 第6回 p~r

関数の使い方

各関数の使い方を記述していきます。詳細な使い方は他の記事に譲るとして、ここでは簡単な使用例を中心に掲載していきます。

enumerate()

引数にイテレーション可能なオブジェクトを渡すとenumerateオブジェクトを返します。
enumerateオブジェクトはイテレータであり、__next__()を呼び出す度に呼び出した回数と元のオブジェクトの要素のタプルを返します。
forループで要素とともにカウンタが欲しい場合などに有用です。

>>> # リスト
... list(enumerate(['a', 'b', 'c']))
[(0, 'a'), (1, 'b'), (2, 'c')]
>>> # ジェネレータ
... list(enumerate(color for color in ['Red', 'Blue', 'Green', 'Yellow', 'Pink']))
[(0, 'Red'), (1, 'Blue'), (2, 'Green'), (3, 'Yellow'), (4, 'Pink')]
>>> # 第2引数startを渡すと、開始する数値を指定できる
... list(enumerate(['five', 'six', 'seven'], start=5))
[(5, 'five'), (6, 'six'), (7, 'seven')]
>>> # forループで使うとき
... for i, name in enumerate(['Taro', 'Jiro', 'Saburo'], start=1):
...     print(i, ':', name)
...
1 : Taro
2 : Jiro
3 : Saburo

eval()

引数をPythonの式として評価します。評価された結果は返り値として取得することができます。
違いが分かりづらいのですが、eval()が実行できるのは「式」であり、「文」の実行にはexec()を使う必要があります。従って、eval('2**10')は1024を返却しますが、eval('x=2**10')は代入文ですのでエラーになります。
式や文の仕様は標準ドキュメントの言語リファレンスに整理されています。
式 (expression), 単純文 (simple statement), 複合文 (compound statement)

>>> # 式の評価
... eval('2**10')
1024
>>> # デフォルトでは実行場所のスコープがeval()のスコープになる
... # したがって変数の参照ができる
... x = 1
>>> eval('x + 1')
2
>>> # 文は実行できないので代入文はエラーになる
... eval('y = 1000')
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<string>", line 1
    y = 1000
      ^
SyntaxError: invalid syntax
>>> # 第2引数globals, 第3引数localsにマッピングを渡すことでコンテキストを定義できる
... # ここでは、globalsにx=10が定義されているので、x=1は参照されない。
... x = 1
>>> eval('x + 1', {'x': 10})
11

eval()はcompile()で生成したコードオブジェクトを実行することもできます。
この場合、複数行の文からなるスクリプトでも、'exec'モードでcompile()しておけばeval()で実行可能です。返り値はNoneです。

>>> source = '''
... import math
...
... def sqrt(number):
...     return math.sqrt(number)
...
... for i in range(10):
...     print(i, ':', sqrt(i))
... '''
>>> result = eval(compile(source, filename='<string>', mode='exec'))
0 : 0.0
1 : 1.0
2 : 1.4142135623730951
3 : 1.7320508075688772
4 : 2.0
5 : 2.23606797749979
6 : 2.449489742783178
7 : 2.6457513110645907
8 : 2.8284271247461903
9 : 3.0
>>> print(result)
None

exec()

eval()と同じように、引数でPythonのコードを渡して実行することができます。
違いは、eval()が式の実行のみができるのに対して、exec()は文の実行をすることができるということです。Pythonファイルとして保存して実行できる文字列であれば、eval()に渡すことができます。1

>>> # Pythonのソースコード文字列を作成
... source = '''
... def foolish_when_3(number):
...     """3の倍数か3がつくとアホになる"""
...     if number % 3 == 0 or '3' in str(number):
...         return 'へ(゚∇ ゚)へ {}!!!!!!'.format(number)
...     return str(number)
...
... # importもできる
... import unittest
...
... class TestAho(unittest.TestCase):
...
...     def test_aho(self):
...         template_expected =  'へ(゚∇ ゚)へ {}!!!!!!'
...         self.assertEqual(foolish_when_3(3), template_expected.format(3))
...         self.assertEqual(foolish_when_3(13), template_expected.format(13))
...         self.assertEqual(foolish_when_3(99), template_expected.format(99))
...
...     def test_not_aho(self):
...         self.assertEqual(foolish_when_3(1), '1')
...         self.assertEqual(foolish_when_3(100), '100')
...
... for i in range(1, 16):
...     print(foolish_when_3(i))
...
... unittest.main()
... '''
>>> # 実行する。
... exec(source)
1
2
( ) 3!!!!!!
4
5
( ) 6!!!!!!
7
8
( ) 9!!!!!!
10
11
( ) 12!!!!!!
( ) 13!!!!!!
14
( ) 15!!!!!!
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

compileした結果を実行することもできます。globals, localsを渡すことでスコープを制御することができます。

>>> source = '''
... x = x * 1000
... print(x)
... '''
>>> # comile結果を渡せる
... compiled = compile(source, '<string>', mode='exec')
>>> # globalsで名前空間を定義できる
... result = exec(compiled, {'x': 5})
5000
>>> # 返り値はNone
... print(result)
None

filter()

引数で渡すリストなどのiterableから条件に合う値だけをフィルタリングすることができます。条件は関数として渡します。
返り値はfilterオブジェクトというイテレータです。

>>> # 関数の結果がTrueのものをフィルタリングする。
... filtered = filter(lambda x: x > 0, [-1, 2, 0, -0.1, 0.2, 10])
>>> list(filtered)
[2, 0.2, 10]
>>> # 関数がNoneの場合は、iterableの要素が真のものだけが残る
... filtered = filter(None, [-1, 2, 0, -0.1, 0.2, 10])
>>> list(filtered)
[-1, 2, -0.1, 0.2, 10]
>>> # ↑ 0が偽として評価されている

>>> # 犬だけ抽出する
... pets = [{'name': 'Pochi', 'type': 'Dog'}, {'name': 'Tama', 'type': 'Cat'}, {'name': 'Gatsby', 'type': 'Parrot'}, {'name': 'Wasabi', 'type' :'Dog'}]
>>> def is_dog(pet):
...     return pet['type'] == 'Dog'
...
>>> list(filter(is_dog, pets))
[{'name': 'Pochi', 'type': 'Dog'}, {'name': 'Wasabi', 'type': 'Dog'}]

float()

浮動小数点型のオブジェクトを生成します。

>>> # 引数には浮動小数点や数値型を渡せる。
... float(10)
10.0
>>> float(1.1)
1.1
>>> # 文字列を渡せる
... float('3.4')
3.4
>>> float('10')
10.0
>>> float('1.02e-3')
0.00102
>>> # Python3.6以降は位の区切り文字に"_"を使える
... float('1_000.05')
1000.05
>>> # 変換できないときはエラーになる
... float('float')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 'float'
>>> # 無限大を表現できる
... float('infinity')
inf
>>> # 負の無限大
... float('-infinity')
-inf
>>> # 正の無限大の加算の結果は無限大
... float('infinity') + float('infinity')
inf
>>> # 無限大の正負の加算はNot a Number
... float('infinity') + float('-infinity')
nan

format()

第1引数にフォーマットしたい値を、第2引数にフォーマット指定文字列を渡すことで変換後の値を返却してくれます。文字列や数値の場合は、str.format()に渡す書式の{}の中身や%-書式の-にあたる部分がフォーマット指定文字列になります。詳細は書式指定ミニ言語仕様を参照してください。
フォーマット指定文字列がどのような形式になるかは、valueに渡したオブジェクトのもつ__format__()関数に依存します。

>>> # 第2引数を渡さない場合はstr(value)と同じになる
... format('value')
'value'
>>> format(10)
'10'
# 多くはstr.formatの書式指定に従う
>>> format('value', 's')
'value'
>>> # 整数型->左0埋め10桁
... format(3456, '0>10d')
'0000003456'
>>> format(1.234, '0=+10f')
'+01.234000'
>>> # 日時型を渡すときはdatetime.strftime()の書式に従う
... from datetime import datetime
>>> format(datetime.now(), '%Y/%m/%d')
'2019/02/04'

frozenset()

iterableを引数にとって、frozensetオブジェクトを返却します。
基本的にset型と同じように集合演算を扱えますが、immutableである点がsetと異なります。
したがって、add()、discard()などの集合の要素を変更する処理はできません。
集合型演算の詳細はset(集合)型を参照してください。

>>> # 引数はiterable
... frozenset((1, 3, 3, 4, 4, 5))
frozenset({1, 3, 4, 5})
>>> frozenset(i % 3 for i in range(10))
frozenset({0, 1, 2})
>>> # 和集合: setとの演算
... frozenset((1, 3, 3, 4, 4, 5)) | {1, 5, 8, 10}
frozenset({1, 3, 4, 5, 8, 10})
>>> # 積集合
... frozenset((1, 3, 3, 4, 4, 5)) & {1, 5, 8, 10}
frozenset({1, 5})

getattr()

第1引数にオブジェクトを、第2引数に属性名を渡すと、オブジェクトに設定されている属性の値を取得することができます。

>>> class Dog(object):
...     def __init__(self, name, blood, sex, secret):
...         self.name = name
...         self.blood = blood
...         self.sex = sex
...         self.__secret = secret
...
>>> mydog = Dog('John', 'Golden Retriever', 'Male', "I ate my master's lunch")
>>> # オブジェクトから属性を取得する
... getattr(mydog, 'name')
'John'
>>> getattr(mydog, 'blood')
'Golden Retriever'
>>> # 存在しない属性名ではエラーになる
... getattr(mydog, 'age')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Dog' object has no attribute 'age'
>>> # シークレットな属性はそのままでは取得できない。
... getattr(mydog, '__secret')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Dog' object has no attribute '__secret'
>>> # マナー違反
... getattr(mydog, '_Dog__secret')
"I ate my master's lunch"

globals()

モジュールのグローバルスコープに設定されている属性の辞書を返却します。

>>> # コンソールでは__name__が'__main__'になっていることがわかる。
... globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}

ここでいうグローバルスコープは、実行される側ではなく定義されている場所の値です。

a.py
x = 10000000

def print_globals(key):
    print(globals()[key])
b.py
from a import print_globals

x = 0

print_globals('x')
print_globals('__name__')

b.pyを実行してみると、x__name__がa.pyでのグローバル値になっていることが分かります。

$ python3.7 b.py
10000000
a

第4回に続く

次回はhasattr()から。


  1. 非常に便利なeval()やexec()ですが、原則的に、Webアプリケーションなどの外部から入力を受け取る部分の処理に使ってはいけません。もしeval()やexec()に悪意のあるコードを渡すことができてしまえば、アプリケーションを壊したり重要な情報を抜き出すようなコードが実行できてしまう可能性があるからです。使わなければいけない場合は、セキュリティ上のリスクがないか注意してください。 

11
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
16