社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

Python twitter 検索APIでキーワード検索

twitter が用意してくれている検索APIをたたいてみます。
この手の記事はたくさんあるので、少し検索すればすぐに方法が見つかります。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from requests_oauthlib import OAuth1Session
import json
import secret

# URL for keyword search
url = 'https://api.twitter.com/1.1/search/tweets.json'

keyword = 'Python'
params = {'q': keyword, 'count': 10}

# GET by OAuth
twitter = OAuth1Session(secret.CK, secret.CS, secret.AT, secret.AS)
req = twitter.get(url, params = params)

if req.status_code == 200:
    timeline = json.loads(req.text)
    statuses = timeline['statuses']
    for each in timeline['statuses']:
        print('-----')
        print(each['text'])
else:
    print ("Error: %d" % req.status_code)

以下の4つの値については、スクリプトに直接記述する訳にはいかなかったので、別ファイル(secret.py)に入れて、読み込むようにしています。
取得方法については、
Twitterアプリケーションの作成(Consumer key、Consumer secret、Access token、Access token secretの確認方法)

CK: Consumer Key
CS: Consumer Secret
AT: Access Token
AS: Accesss Token Secert

とりあえず、キーワードは 'Python' で直接スクリプトに書いていますが、引数で渡すなりすれば良いと思います。
10件取得して、ツイート本文のみを出力しています。

実行結果
-----
Top Stories, Jun 4-10: Did Python declare victory over R?; The Keras 4 Step Workflow https://t.co/NtfUBmnZDO
-----
RT @nixcraft: "Haskell is faster than C++, more concise than Perl, more regular than Python, more flexible than Ruby, more typeful than C#,…
-----
😇 ☑ 😇 @timewith_uk need a Senior software engineer https://t.co/UzX2jm76UG #python #flask
-----
tag yourself are you alm's inner conflict, lukas' anger, forsyth's anguish or python's chivalry https://t.co/6Hn3BsZhPs
-----
Building your own cryptocurrency trading bot using Python and the Poloniex API https://t.co/KPoXcH5kXQ
-----
Practical XGBoost in Python - 2.6 - Handle Imbalanced Dataset https://t.co/eWWkV1jRwY via @YouTube
-----
Building your own cryptocurrency trading bot using Python and the Poloniex API: https://t.co/K8pBDkEnXM via @YouTube
-----
RT @gp_pulipaka: Building a Serverless Alerting Chatbot with AWS Lex. #BigData #MachineLearning #DataScience #AI #CloudComputing #Serverles…
-----
Network Test Engineer: Network Test Engineer (Mountain View, CA) Design, develop, modify, &/or test network needed… https://t.co/Fij8REd3ga
-----
@Bugseedz @LOUwwHENRY @LeLapinTaquin Rien qu'au niveau du champ lexical utilisé on a compris d'où ça vient

たまに、キーワードが含まれていないものが出力されている気がするが何故だろう。

Mockito 一部のメソッドのみをモック化する(Java モック)

ケース

例えば、テストが不可能なメソッド(外部API接続している)のみをモック化して、その他のメソッドはそのまま動作させたい場合など。

方法

Mockito の @Spy を使うと良いらしいです。
@Mock では、全てのメソッドがモック化される。
@Spy では、一部のメソッドのみをモック化することができる。

テスト対象のクラス
public class Member {
	private PointCard pointCard;
	
	public Member() {
		this.pointCard = new PointCard();
	}

	public String getMemberNameAndPoint() {
		return pointCard.getMemberName() + "_" + pointCard.getPoint();
	}
}
モック化するクラス
public class PointCard {
	public PointCard() {
	}
	
	public String getMemberName() {
		return "hoge";
	}
	
	public long getPoint() {
		return 1000L;
	}
}
テスト
public class MemberTest {
	@Spy
	PointCard pointCardMock = new PointCard();  // 1
	
	@InjectMocks
	Member memberMock = new Member();  // 2

        @Before
	public void setup() {
          MockitoAnnotations.initMocks(this);  // 3
	}

        @Test
	public void testGetMemberNameAndPointWithSpy() {
          doReturn(7777L).when(pointCardMock).getPoint();  // 4
          assertThat(memberMock.getMemberNameAndPoint(), is("hoge_7777"));  // 5
	}
}

1. モックオブジェクト(ここでは、PointCard)を生成
2. インジェクト先(テスト対象のクラス。ここでは、Member)を指定
3. インジェクトするための初期化
4. モック(PointCard)の振る舞いを指定
5. 動作確認
getMemberName() メソッドはモック化されておらず、getPoint() メソッドのみがモック化されている。
もし、@Mock を適用していた場合、getMemberName() メソッドもモック化されるが、戻り値(振る舞い)を定義していないため、null が返却される動作となる。

所感

個人的には、@Mock と @Spy が混在して、何がモックで何がモックでないのかよく分からなくなってしまった時があって、全て @Mock で頑張っていました。
ただ、こうやって使用してみると @Spy はやはり便利に感じます。
変に混在しそうになったら、テストクラス自体を分けてあげることで整理しやすくなるかもしれません。

Mockito 取得したオブジェクトのメソッドの戻り値をモックする

@Test
public void testGetMemberInfoWithNestedObjectMock() {
 Member member = mock(Member.class, RETURNS_DEEP_STUBS);  // 1
	
 when(member.getPointCard().getPoint()).thenReturn(3000L);  // 2
	
 assertThat(member.getPointCard().getPoint(), is(3000L));  //3
}

1. Member クラスのモックオブジェクトを作成する
このとき、第2引数として RETURNS_DEEP_STUBS を指定しています。

2. モックの振る舞いを定義する
具体的には、まず member.getPointCard() で PointCard というオブジェクトが取得されると仮定しています。
そして、PointCard の getPoint() メソッドを呼び出したとき、3000L を返却するように定義しています。

3. 検証する
定義したモックの振る舞い通りに値が返却されていることを確認しています。

所感

実は今まで、RETURNS_DEEP_STUBS を使ったことなかったですが、ネストしたオブジェクトのメソッドの戻り値を任意に設定できるのは、とても便利だと思います。
もし、RETURNS_DEEP_STUBS を指定していなかったら、NullPointerException がスローされます。

Mockito 引数に応じて返す値を変化させる(Java モック)

テスト(モック)対象のクラス

誕生月のクラス。
誕生石を返却するメソッドも持っているが、誕生石を保持するためのクラス変数はない。
誕生月を引数にしているが、必ず null が返却されるようになっている。

public class BirthMonth {
	private int month;
	
	public BirthMonth(int month) {
		this.month = month;
	}
	
	public int getMonth() {
		return month;
	}
	
	public String getBirthStone(int month) {
		return null;
	}
}
テストコード(JUnit)
public class BirthMonthTest {
        # モックなし
	@Test
	public void testGetBirthStoneWithoutMock() {
		BirthMonth birthMonth = new BirthMonth(2);
		assertThat(birthMonth.getBirthStone(2), is(nullValue()));
                assertThat(birthMonth.getBirthStone(7), is(nullValue()));
                assertThat(birthMonth.getBirthStone(11), is(nullValue()));
	}
	
        # モックあり
	@Test
	public void testGetBirthStoneWithMock() {
		BirthMonth birthMonth = mock(BirthMonth.class);
		
		when(birthMonth.getBirthStone(anyInt())).thenReturn("not found");
		when(birthMonth.getBirthStone(2)).thenReturn("amethyst");
		when(birthMonth.getBirthStone(7)).thenReturn("ruby");
		when(birthMonth.getBirthStone(11)).thenReturn("topaz");
		
		assertThat(birthMonth.getBirthStone(5), is("not found"));
		assertThat(birthMonth.getBirthStone(2), is("amethyst"));
		assertThat(birthMonth.getBirthStone(7), is("ruby"));
		assertThat(birthMonth.getBirthStone(11), is("topaz"));
		assertThat(birthMonth.getBirthStone(20), is("not found"));
	}
}

モックなしの場合、getBirthStone() の引数にどんな値を与えても、null が返却される。
モックありの場合、getBirthStone() の引数に与える引数に応じて、返却する値を定義することが出来る。

所感

anyInt() など、anyXXX でデフォルト値を設定してあげることができるイメージ。
ただし、anyXXX は最初に定義してあげないと、java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch が発生してしまう模様。

Mockito あるメソッドが任意の値を返す(Java モック)

何も特別なことはありません。最近、色んなモックライブラリを混同してしまっているので、復習を込めて確認しています。

テスト(モック)対象のクラス

仮に以下のようなクラス(Member)があった場合、getPointCard() の返り値(PointCard オブジェクト)を任意にしたい。

public class Member {
	private int id;
	private String name;
	private PointCard pointCard;
	
	public Member(int id, String name) {
		this.id = id;
		this.name = name;
		this.pointCard = new PointCard(id, name, 1000);
	}
	
	public PointCard getPointCard() {
		return pointCard;
	}
}
public class PointCard {
	private int cardNumber;
	private String memberName;
	private long point;
	
	public PointCard(int cardNumber, String memberName, long point) {
		this.cardNumber = cardNumber;
		this.memberName = memberName;
		this.point = point;
	}
	
	public int getCardNumber() {
		return cardNumber;
	}
	
	public String getMemberName() {
		return memberName;
	}
	
	public long getPoint() {
		return point;
	}
}

テストコード(JUnit)

public class MemberTest {
        # モックなし
	@Test
	public void testGetMemberInfoWithoutMock() {
		Member member = new Member(1, "hoge");
		assertThat(member.getPointCard().getPoint(), is(1000L));
	}
	
        # モックあり
	@Test
	public void testGetMemberInfoWithMock() {
		Member member = mock(Member.class);
		PointCard pointCard = new PointCard(1, "hoge", 2000L);
		
		when(member.getPointCard()).thenReturn(pointCard);
		
		assertThat(member.getPointCard(), is(pointCard));
		assertThat(pointCard.getPoint(), is(2000L));
	}
}

モックなしの場合、Member クラスのコンストラクタにて、point = 1000 で生成された PointCard のオブジェクトがそのまま返却される。
モックありの場合、返却する PoinrCard のインスタンスがテストコード内で改めて生成(point = 2000 にて)されており、その新たに生成されたオブジェクトが返却される。

所感

when(member.getPointCard()).thenReturn(pointCard); のように、”○○メソッドが呼ばれた時、××を返却する” と直感的に書けるところが分かり易い。

クラス概略図

f:id:blueskyarea:20180607234854p:plain

JVM はどのようにプログラムを実行しているのか

Runtime Data Area に取り込まれたバイトコードは、Execution Engine と呼ばれるシステムで実行されているようです。
バイトコードを読み込み、少しずつ実行していきます。

Execution Engine も複数の仕組みで構成されています。

Execution Engine

1. Interpreter

この Interpreterバイトコードの解釈は速いですが、実行速度は遅いです。
不利な点として、1つのメソッドが何度も呼ばれる場合、毎回新しく解釈が行われる点です。

2. JIT Compiler

JIT Compiler は Interpreter の不利な点を解消してくれます。
Execution Engine は Interpreter によってバイトコードを変換していきます。
ただし、もし繰り返しコードが見つかるとJITコンパイラが使用され、バイトコード全体をコンパイルしてネイティブコードに変換します。
このネイティブコードは、システムのパフォーマンスを向上させるため、繰り返しメソッド呼び出し時に直接使用されます。

更に、この JIT Compiler は複数の機能によって構成されているようです。
1. Intermediate Code generator
中間コードを生成します。

2. Code Optimizer
中間コードの最適化を行います。

3. Target Code Generator
機械語、もしくはネイティブコードを生成します。

4. Profiler
あるメソッドが複数回呼び出されているかどうかなど、ホットスポット’(JVMのHotSpotとは違う)を見つける。

3. Garbage Collector

参照されていないオブジェクトを集めて、取り除きます。
"System.gc()" でも呼び出されますが、特に保証されていない動作です。

その他の構成要素として、
Java Native Interface (JNI) は Native Method Libraries と相互作用し、Execution Engine が必要とする Native Libraries を提供する。
Native Method Libraries は、Execution Engine が必要としている Native Libraries の集まり。

所感

実行パートにおいて、Interpreter と Compiler が上手く共同作業しているのだと感じる。
final 修飾子が付いた変数やメソッドは、JIT Compiler に最適化のヒントを与えているという記事を見たことがあるが、本当だろうか。
ここで知る限りは、繰り返し呼ばれるかどうかがポイントであって、final であるかどうかはそれほど大きな影響はないのかもしれない。

f:id:blueskyarea:20180607010901p:plain

JVM はどのようにデータを保持しているのか

Runtime Data Area と呼ばれる場所に保持されるようになっている。
そこは保持するデータの種類によって、大きく5つに分類されている。

Runtime Data Area

1. Method Area

全ての class データが保持される。
static 変数もここに保持される。
1 つの JVM に対して、ただ一つの Method Area であり、複数のスレッド間でリソースを共有している。
そのため、スレッドセーフではない。

2. Heap Area

全ての Object が保持される。
インスタンス変数や配列(array) などもここに保持される。
1 つの JVM に対して、ただ一つの Heap Area であり、複数のスレッド間でリソースを共有している。
そのため、スレッドセーフではない。

3. Stack Area

プログラム実行中、各スレッドに対しては runtime stack と呼ばれるものが作られる。
各メソッドが呼ばれると、Stack Frame と呼ばれるメモリ領域内に、その stack が生成される。
全てのローカル変数は、ここに生成される。
この領域は共有されていないため、スレッドセーフである。
この Stack Frame は更に3つに分類することができる。

1. Local Variable Array
メソッドに関連するローカル変数の数と、対応する値がここに格納される。

2. Operand stack
何らかの中間処理が必要なとき、この領域が使われる。

3. Frame data
メソッドに対応するすべてのシンボルがここに格納される。
例えば、例外処理においては、catch ブロックの情報がここに格納される。

4. PC Registers

各スレッドがそれぞれ保持するカウンタのような領域。
現在実行中のアドレスや、次の命令のアドレスを保持する。

5. Native Method stacks

ネイティブメソッドの情報を保持している。
各スレッドそれぞれ別々の領域が生成される。

所感

Heap Area については、有名でパフォーマンス等で意識することはあった。
その他については意識していなかったが、細かなパフォーマンスチューニングにおいて、重要な項目になり得ると感じた。
Java のバージョンに依って、若干の仕様が異なる可能性があるが、それぞれの役割について理解しておきたい。

f:id:blueskyarea:20180605233542p:plain