##はじめに
このあいだ@TakesxiSximadaさんがPythonでパスワードの強度をしらべるライブラリSafeのコードリーディングという記事をあげていて面白そうだったので、自分もやってみました。
そこで以前からちょっと使ってみようと気になっていたfakerというライブラリを取り上げてみました。
fakerはダミーなテストデータをいい感じに生成してくれるライブラリです。他の言語でもよく見かけるやつのpython版です。
https://pypi.python.org/pypi/fake-factory/0.5.3
https://github.com/joke2k/faker
##Install
pipでインストールできます。
$ pip install fake-factory
##READMEのsampleコードを試す
※ 実行環境のpythonバージョンは3.5.0です。
とりあえずimport。
>>> from faker import Factory
そしてテストデータを作ってくれるgeneratorを生成すればOK。
>>> fake = Factory.create()
あとはこんな感じでテストデータが返ってきます。
>>> fake.name()
'Anfernee Reichel'
>>> fake.address()
'084 Tiney Fork Suite 757\nPort Earl, MI 20240-1776'
>>> fake.text()
'Facilis non eligendi qui deleniti ullam est. Ab minus est non et occaecati laborum sequi. Vero consectetur repellendus dicta velit. Quisquam omnis alias error sed totam.'
多言語化にも対応していて、さっきのFactory.create()
にlocale
を引数に渡してあげることで実現できます。
>>> fake = Factory.create('ja_JP')
>>> fake.name()
'津田 裕美子'
>>> fake.address()
'群馬県中央区芝公園32丁目22番3号 上広谷ハイツ400'
>>> fake.text()
'Non ut in unde ipsa fugiat excepturi voluptate. Enim molestias voluptatem aperiam. Est fuga distinctio sit officia qui velit numquam sint.'
textには日本語データが用意されてなかったので、デフォルトのen_US
のデータが返ってきてます。
ちなみに'fakeは
faker.generator.Generator()`のインスタンスです。
>>> type(fake)
<class 'faker.generator.Generator'>
##コードリーディング
それではコードを読んでいきたいのですが、
その前にfakerではProviderが何者なのかを理解できた方が話が早いので、まずProviderに関して説明していきます。
###Provider - テストデータを提供してくれる
各Providerは faker/faker/providers 配下に格納されてます。
├── providers
│ ├── __init__.py
│ ├── __pycache__
│ ├── address
│ ├── barcode
│ ├── color
│ ├── company
│ ├── credit_card
│ ├── currency
│ ├── date_time
│ ├── file
│ ├── internet
│ ├── job
│ ├── lorem
│ ├── misc
│ ├── person
│ ├── phone_number
│ ├── profile
│ ├── python
│ ├── ssn
│ └── user_agent
address
やbarcode
などのカテゴリ毎に各言語に対応したディレクトリと、各カテゴリのBaseとなるProviderを実装しています。
ここではperson
に注目して、ソースを追っていきます。
person
配下はこんな感じになってます。
├── providers
│ ├── __init__.py
│ ├── person
│ │ ├── __init__.py
│ │ ├── bg_BG
│ │ ├── cs_CZ
│ │ ├── de_AT
│ │ ├── de_DE
│ │ ├── dk_DK
│ │ ├── el_GR
│ │ ├── en
│ │ ├── en_US
│ │ ├── es_ES
│ │ ├── es_MX
│ │ ├── fa_IR
│ │ ├── fi_FI
│ │ ├── fr_FR
│ │ ├── hi_IN
│ │ ├── hr_HR
│ │ ├── it_IT
│ │ ├── ja_JP
│ │ ├── ko_KR
│ │ ├── lt_LT
│ │ ├── lv_LV
│ │ ├── ne_NP
│ │ ├── nl_NL
│ │ ├── no_NO
│ │ ├── pl_PL
│ │ ├── pt_BR
│ │ ├── pt_PT
│ │ ├── ru_RU
│ │ ├── sl_SI
│ │ ├── sv_SE
│ │ ├── tr_TR
│ │ ├── uk_UA
│ │ ├── zh_CN
│ │ └── zh_TW
次に、/faker/providers/person
直下の__init__.py
を見てみます。
from .. import BaseProvider
class Provider(BaseProvider):
formats = ['{{first_name}} {{last_name}}', ]
first_names = ['John', 'Jane']
last_names = ['Doe', ]
def name(self):
"""
:example 'John Doe'
"""
pattern = self.random_element(self.formats)
return self.generator.parse(pattern)
@classmethod
def first_name(cls):
return cls.random_element(cls.first_names)
@classmethod
def last_name(cls):
return cls.random_element(cls.last_names)
# 以下省略
こんな感じで各言語のPersonProviderのベースとなるProviderが実装されてます。
random_element()
などランダムにデータを抽出するclassmethod群を実装したBaseProviderを継承しているのがわかります。
そして、このProviderを継承してpropertyとmethodを新たに作ったり、オーバーライドしたりして各言語に対応したProviderを用意していきます。
日本語対応のPersonProviderは以下を参照ください。
https://github.com/joke2k/faker/blob/master/faker/providers/person/ja_JP/__init__.py
###Factory.create() - Generatorを生成する
https://github.com/joke2k/faker/blob/master/faker/factory.py#L14-L44
このメソッドでは<class 'faker.generator.Generator'>
のインスタンスを生成してreturnしてます。
下記の処理で、Factory.create()
の引数として渡したlocale
を元に、<class 'faker.generator.Generator'>
のインスタンスであるfaker
に各Providerをセットしています。
(指定したlocale
に対応したProviderがない場合は、DEFAULT_LOCALEであるen_US
のものがセットされます)
for prov_name in as:
if prov_name == 'faker.as':
continue
prov_cls, lang_found = cls._get_provider_class(prov_name, locale)
provider = prov_cls(faker)
provider.__provider__ = prov_name
provider.__lang__ = lang_found
faker.add_provider(provider)
それでは次に、上記の処理で出てきたadd_provider(provider)
を見ていきましょう。
Generator.add_provider() - Generatorにformatを追加する
引数で渡されたProvider(ex. <faker.providers.person.ja_JP.Provider>
)で定義されているpublicなmethodをGeneratorのformatに追加していってます。
Generator.set_formatter() - setattr()のラッパー関数
https://github.com/joke2k/faker/blob/master/faker/generator.py#L70-L75
Generator.add_provider()
のところで急にformatという言葉が出てきましたが、ただGeneratorインスタンスに対してsetattr()を行っているだけです。
まとめ
ここまで見てきたようにFactory.create()
を行うことで、
各言語のProvider群で定義されているpublicなmethodを全てattributesにセットされた<class 'faker.generator.Generator'>
のインスタンスが取得できるようになってます。
このおかげで、以下のようにfake.method_name()
と呼び出すだけで、各言語のProviderの中で実装されているmethod_name()
が実行されてランダムなテストデータを取得することができてます。
>>> fake.name()
'Anfernee Reichel'
##さいごに
力尽きてFactory.create()
の部分しか追えていませんが、Generatorの生成方法が理解できればこのライブラリの他の使い方もわかると思います。
こういった薄いライブラリを取り上げてのコードリーディングは、取っ付きやすく楽しかったのでオススメです!
##あとがき
この記事を書いている途中で、
『ja_JP
のPersonProviderはname()
のformatを日本語で保持していたので、user_name()
とdomain_word()
がきちんと表示されない』
という問題に遭遇しました。
https://github.com/joke2k/faker/blob/master/faker/providers/internet/__init__.py#L27-L32
https://github.com/joke2k/faker/blob/master/faker/providers/internet/__init__.py#L90-L95
上記の問題に対処するためのPRを出して、無事mergeしてくれました。
https://github.com/joke2k/faker/pull/300