UdemyでPythonを勉強した結果を残すブログ。

40歳でプログラミング始めて転職までいけるのかを実録してみます。

8月目標

最近ブログ書けてないので月初めに目標をたてて遂行していこうかと。

 

8月目標

  1. Reactを使ってポートフォリオを作成(youtubeをみながら)
  2. 本業でjavascriptを使って勉強しつつ貢献する(アイデアはqiitaに書く)
  3. python記事も週1ペースでUdemyのを補完していく
  4. paizaに登録したのでランクを上げていく

 

実務未経験でpythonだけは求人も少なくて狭き門だと感じたので、

フロントエンドでReactとJavascriptを7月はやっていっていたのですが

今月はfirebaseとかを使ってreactでデプロイまでできればと計画中。

 

また客観的に評価をしてもらいたいのでfindyでのランク付以外にも

paizaを使ってランクを上げていく。

paizaの試験なんか質問がクセがないか?回答コードが簡単なんだけど

問題を理解するのに時間かかる笑

freeCodeCampで正規表現を勉強する!

引き続きfreeCodeCampで正規表現も勉強。

matchとかもそうだけどエディタで検索するときにも使えるので

正規表現は結構使えるとよさそう。

検索したい文字の後ろ/後にiをつけると大文字小文字関係なく検索してくれる
/後ろにgをつけると同じ文字が複数はいったらリストで返してくれる
let myString = "freeCodeCamp";
let fccRegex = /freeCodecamp/i;
let result = fccRegex.test(myString);
console.log(result);

 

スペルミスなどやある程度おおまかな検索をしたい場合は.をつける。

.testでしか使えない?

let exampleStr = "Let's have fun with regular expressions!";
let unRegex = /.un/;
let result = unRegex.test(exampleStr);

 

の中に検索したい文字を入れるとその文字があると出力される。
[aoe]ならa,o,eぞれぞれ検索してくれるし、[a-z]とかならアルファベット全てを検索してくれる。
数字も可能で両方合わせることも可能。/[a-c4-6]/giならabc、456を検索してくれる。
giなので大文字小文字どちらも、同じのもリストに入れてくれる。

 

let quoteSample = "Beware of bugs in the above code; I have only proved it correct, not tried it.";
let vowelRegex = /[aeiou]/gi;
let result = quoteSample.match(vowelRegex);

 

検索されないようにするには^を先に入れる。

/[^abe0-3]/gi

 

同じ文字が続く場合は検索したい文字の後ろに+をつけるとまとめて検索してくれる。
s+だとsが1個以上検索、sが2個以上の場合はss+を入力

let difficultSpelling = "Mississippi's";
let myRegex = /s+/g;
let result = difficultSpelling.match(myRegex);
console.log(result); //[ 'ss', 'ss', 's' ]を返す

 

後ろに何個ついてようが個数を気にせず検索する場合は*を使う

let soccerWord = "gooooooooal!";
let goRegex = /go*/;
soccerWord.match(goRegex);

 

^はの中にいれると検索されないようにする機能を持つが、[]がないと文章の最初という意味になる。
その検索文字が最後の場合は$を検索文字の後ろにつける。

 

let text = "Go to school";
let text2 = "school to go";
let regex = /^school/;
let regexLast = /school$/;

console.log(regex.test(text)); //false
console.log(regex.test(text2)); //true
console.log(regexLast.test(text)); //true
console.log(regexLast.test(text2)); //false

 

ショートハンド

 

[A-Za-z0-9_]を全て含むのがめんどくさい場合は/\w/だけでショートカットできる
この場合はアンダーラインも含む逆に含まない場合は大文字でショートカットできる/\W/

let quoteSample3 = "The five boxing wizards jump quickly.";
let alphabetRegexV2 = /\w/g;
let result3 = quoteSample3.match(alphabetRegexV2).length;

数字のみのショートカットは\d含まない場合は\DでOK

複雑なフィルター例
let username = "JackOfAllTrades";
let userCheck = /^[a-z][a-z]+\d*$|^[a-z]+\d\d+$/gi;
let result = userCheck.test(username);
最初はアルファベット2文字以上で最後が数字いくつでももしくは最初の1文字以上がアルファベットで最後が2つ以上の数字で終わる

スペースを検索する場合は\s含めない場合は\Sで
同じ文字がある範囲の数ある場合は検索したい文字の直後に{n,n}で検索できる
最後の数字を入れなかったらn以上となる{3,}=3以上
{3}とコンマもない場合はちょうど3つということになる
let sam = "Saaaaaaam";
let countA = /Sa{3,7}m/; //aが3~7個ある場合
let result2 = countA.test(sam);
console.log(result2);

 

 

?の使い方

?を直前の文字に入れるとその文字が0〜1個あれば検索対象に入る

let american = "color";
let british = "colour";
let rainbowRegex= /colou?r/;
rainbowRegex.test(american);
rainbowRegex.test(british);

(?=moji)で入力するとmoji部分が曖昧でも対象にしてくれる

let sampleWord = "astronaut";
let pwRegex = /(?=\w{6,})(?=\w*\d{2})/i;
let result = pwRegex.test(sampleWord);
console.log(result);

 

検索対象をグループしたい場合は()の中に入れる

let myString = "Eleanor Roosevelt";
let myRegex = /(Eleanor|Franklin D.)/;
let result = myRegex.test(myString);

特定しないがある文字数字の繰り返しを検索したい場合は/(\w+)/でできるまたそれを繰り返したい場合 \1 で繰り返しができる
単語を指定しなくていいので汎用性がある
(\w+)は変数みたいなもので.replaceとかで使いたい場合は$1,$2で対応できる

let repeatRegex = /(\w+) \1 \1/;
repeatRegex.test(repeatStr); // Returns true
repeatStr.match(repeatRegex); // Returns ["row row row", "row"]
matchで使うとリストで出力されるが1つ目が検索されたもの、2つ目が検索された単語を出力してくれる

let repeatNum = "42 42 42";
let reRegex = /^(\d+)\s\1\s\1$/; // 確実に数字が3つ、間に半角スペースがあるものを検索してくれる
let result = reRegex.test(repeatNum);

 

検索したものを変更したい場合は.replaceを使う。正規表現と合わせて使う

let str = "one two three";
let fixRegex = /^(\w+)\s(\w+)\s(\w+)$/; // one=$1, two=$2, three=$3となる
let replaceText = "$3 $2 $1"; // ""をの中にいれる
let result = str.replace(fixRegex, replaceText); //three two one

最初と最後のスペースを削除する方法
let hello = " Hello, World! ";
let wsRegex = /^\s+|\s+$/g; // 最初と最後に1つ以上のスペースがあれば抽出する
let result = hello.replace(wsRegex, ""); // 抽出したスペースを全てなくす
console.log(result);

 

 

 

 

freeCodeCampでjavascriptを勉強する!ES6編

javascriptの基礎からES6も勉強。

アロー関数とか「なにこれ?」状態だったので、

今回これを勉強できてかなりためになった。

無料というのがすごい。

 

function部分の省略

 

functionは省略できる
const myFunc = function() {
const myVar = "value";
return myVar;
}
これをES6にすると
const myFunc = () => {
const myVar = "value";
return myVar;
}
となるさらにfuctionの中身がreturn xxx;だけの場合はさらに省略できる
const myFunc = () => "value";
()部分に引数をいれて少し複雑なreturnも可能
const myConcat = (arr, arr2) => arr.concat(arr2);
()部分の引数にはデフォルトを設定することが可能
const increment = (num, val = 1) => num + val;
increment(1,4);

 

引数が固定でない場合でも対応できるargumentオブジェクト

()部分には...argsが使える引数が固定でない場合に有効
const sum = (...args) => args.reduce((a,b) => a + b, 0);

...argsは配列を展開してくれる役目も持つ
const arr1 = [1,2,3,4,5];
const arr2 = [3,4,5];
const arr1args = [...arr1];
const arr2args = [...arr2];
console.log(arr1);
console.log(arr2);

 

同じオブジェクトで複数を定義する場合このように省略できる
元データソース
const HIGH_TEMPERATURES = {
yesterday: 75,
today: 77,
tomorrow: 80
};

省略記述
const {today ,tomorrow} = HIGH_TEMPERATURES;
別の名前に定義することもできる
const {today: highToday, tomorrow: highTomorrow} = HIGH_TEMPERATURES;

入れ子になっている場合でも複数定義できる
const LOCAL_FORECAST = {
yesterday: { low: 61, high: 75 },
today: { low: 64, high: 77 },
tomorrow: { low: 68, high: 80 }
};
const {today: {low: lowToday2, high: highToday2 }} = LOCAL_FORECAST;
// lowToday2 = 64, highToday2 = 77 という2つの引数ができる


 

 

ES6ではテンプレートリテラルが使える

pythonでいうprint(f"{name} is {age}")みたいなもの

 
ES6の場合は``を使うバックティック (クオート'',""ではない
const result = {
success: ["max-length", "no-amd", "prefer-arrow-functions"],
failure: ["no-var", "var-on-top", "linebreak"],
skipped: ["no-extra-semi", "no-dup-keys"]
};
function makeList(arr) {
// Only change code below this line
const failureItems = [];
for (let i = 0; i < arr.length; i++){
failureItems.push(`<li class="text-warning">${arr[i]}</li>`);
}
// Only change code above this line
return failureItems;
}
const failuresList = makeList(result.failure);
console.log(failuresList);


 

 

ファンクションの変数と引数が同じ場合は省略できる。
const createPerson = (name, age, gender) => {
// Only change code below this line
( return {
name: name,
age: age,
gender: gender
};)
// Only change code above this line
};この場合


const createPerson = (name, age, gender) =>
// Only change code below this line
({name, age, gender});
// Only change code above this line
のように省略できる

 

 

オブジェクト内でのファンクションの省略
const person = {
name: "Taylor",
sayHello: function() {
return `Hello! My name is ${this.name}.`;
}
};
省略すると
const person2 = {
name: "Taylor",
sayHello() {
return `Hello! My name is ${this.name}.`;
}
};

 

 

class表記ができるように
class SpaceShuttle {
constructor(targetPlanet) {
this.targetPlanet = targetPlanet;
}
}
const zeus = new SpaceShuttle('Jupiter');

 

classを使う時は最初にconstructorを記述して初期値を設定する。

pythonでいうdef __init__みたいなもの

 

 

type="module"で外部jsをインポートができる
es6だと他のjsファイルからモジュールとして追加ができるようになる
typeをmoduleにするだけでimportができるようになる?

<script type="module" src="filename.js"></script>

exportするjsには最後にexport{exportする変数・引数}を入れる
const uppercaseString = (string) => {
return string.toUpperCase();
}
const lowercaseString = (string) => {
return string.toLowerCase()
}
export { uppercaseString, lowercaseString};

ファイルの一つの返しのみをインポートする場合は
export default をつける reactでもやってた

export default function (x, y) {
return x - y;
}
名前付きでもOK
export default function add(x, y) {
return x + y;
}

importは{}なしでもいけるちょっとpython
import add from "./sample.js";

 

promise文で処理を変えたり順序を変更できるようになる


Promise((resolve,reject) => {})のあとにしたい処理があったら
最後に.then(result => {処理});を追加するとできる
Promiseでrejectになった時どうするかの処理は
.catch(error => {console.log("エラーが発生しました")})みたいな感じで追加できる
pythonでいうtry exceptみたいな感じかと思う

 

ES6は特に覚えたかった部分のjsなので、だいぶ勉強になりました。

 

FreeCodeCampでjavascriptを勉強して簡単にまとめてみる。

一通りpythonの基礎はできてきたかなと思って求人を色々見ていると

関西とかでは実務なしのpythonエンジニアはかなり狭き門と感じたので、

フロントエンドもかじってみようとjavascriptも始めることに。

 

書き方はpythonと似たようなところがあるのでそんなに違和感なくできてる感じです。

とりあえず無料でもしっかり勉強できそうなところで

英語がメインではありますがfreeCodeCampで初歩からスタート。

https://www.freecodecamp.org/learn

 

 

とりあえず基礎をメモ的にまとめてみる。

変数定義

前はvarを使っていたがローカルにおいてもグローバルに使えてしまってややこしいので

今はletやconstを使う。

letは定義したものは変更できるけど、

constは基本的にはできない。(配列を定義した場合、配列内のデータは変えられる)

 

for文、while文、do while文

for文はほぼpythonにも似てる感じ。

let jsnum = 0;
for ( let i = 0; i < 10; i++) {
jsnum = i++;
}
console.log(jsnum);

 

pythonなら

pynum = 0
for i in range(10):
pynum += 1
print(pynum)


while文

js

let jsnum = 0;

while(jsnum < 10) {
jsnum += 1;
}
console.log(jsnum);

 

python

pynum = 0
while pynum < 10:
pynum += 1
print(pynum)

 

jsにはdo whileというwhileに入る前に1回whileの処理ができる構文がある

let jsnum = 0;

do {
jsnum += 1;
console.log(jsnum);
}
while(jsnum < 10);
console.log(jsnum);

 

再起関数

pythonではわからなかったけどjsで勉強してようやくわかった。

関数の中に自身の関数を入れてforを使わず回転させる。


function arrayLength(array) {
if(_.isEmpty(array)) { // isEmpty : 配列要素が空ならtrueを返す
return 0;
} else {
return 1 + arrayLength(_.rest(array)); // rest : 配列から先頭要素を除いた配列を返す
}
}
arrayLength(nums); // 5

複数のループもできる
function countdown(n) {
if (n < 1) {
return [];
} else {
const arr = countdown(n - 1);
arr.unshift(n);
return arr;
}
}
 
function rangeOfNumbers(startNum, endNum) {
if (endNum - startNum === 0) {
return [startNum];
} else {
const arr = rangeOfNumbers(startNum, endNum - 1);
arr.push(endNum);
return arr;
}
};

rangeOfNumbers(1,4); // [1,2,3,4]が出力される
 

 

 

if elseを使わず分岐ができる 構文

if/elseを使わずtrueかfalseで動作を分けたい場合は? a :b でできる
return a > b ? "a is true": "b is false";
条件は複数追加することも可能
function checkSign(num) {
return (num == 0) ? "zero" : (num > 0) ? "positive" : "negative";
}
checkSign(10);

 

 

時々出てきたこの構文よくわかってなかったけどこれで納得。

 

 

その他よく使うファンクション
.push()は配列を最後に追加
.pop()は配列の最後のデータをとる
.shift()は最初のデータをとる
.unshift()はデータを配列一番最初に追加する
a.concat(b) 配列a,bを結合する
const sumWithInitial = array1.reduce*1; // 19

 

 

freeCodeCampで無料でここまで勉強できるのはすごい!

英語もそんなに難しい英語じゃないのでやりやすいと思います。

 

次はES6まとめ。

 

 

 

*1:previousValue, currentValue) =>

previousValue + currentValue, initialVal);
// reduceは対象の配列を取得して、順番に計算する例えばarray1 = [3,4,5]の場合
// 今回は3 + 4 + 5=12を出力する、
// 別にデフォルト数値をinitialValの部分で決められる。なくても動く
const map1 = a.map(x => x + 1); // aが配列[1,3,4]だとしたら配列にある数値を全て取得して
// 例の場合それぞれ+1をする map1は[2,4,5]となる

Math.random()で0〜1の間の小数点でランダムに数字を出す 0,4984320493094とか
Math.floorは小数点をカットする

parseInt()はstringの数を整数に治す parseInt(n, 2)にすると二進法で変換される
function convertToInteger(str) {
return parseInt(str, 2);
}
console.log(convertToInteger("10011"

DAY36 めちゃくちゃ下げた米国の個別株の理由をニュースで知るアプリを作る!

DAY35抜かしちゃったけどAPIの呼び出しの練習なので、

DAY36に飛んで実践。

 

後日やるけど試したかったのでスクレイピングもつけています。

今回はスクレイピング部分の説明はなしでやります。

 

米国株の値動きがわかるAPIから今日と昨日の価格を取得して、

暴落していたらその理由がないかニュースAPIでニュースを検索して表示する、

というようなツールを作ります。

 

暴落しても全体の地合いで個別でニュースがない場合もありますが。。。

 

 

では始めます。

 

from get_company_name import get_company_name
import requests
#dotenv-templete
import os
import datetime as dt
from dotenv import load_dotenv
from os.path import join, dirname
load_dotenv(verbose=True)
dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
#dotenv-templete

STOCK_NAME = input("Type Company Code eg.TSLA : ")
STOCK_API = os.getenv("STOCK_API")
STOCK_ENDPOINT = "https://www.alphavantage.co/query"
NEWS_API = os.getenv("NEWS_API")
NEWS_ENDPOINT = "https://newsapi.org/v2/everything"

today_news = dt.datetime.today()
today_news = today_news.strftime("%Y-%m-%d")

company_name = get_company_name(STOCK_NAME)
print(company_name)

 

最初の2行はAPIjsonを取得するためのrequestsと、

yahooファイナンスから英語の企業名を取得するスクレイピングの自作コードをインポートしてます。

 

次はAPI関連の情報で、STOCK_NAMEはinputで自分が気になる会社コードを

入れられるようにしています。

 

today_news部分は今日の日付を取得してNEWS APIのパラメータに合わせたフォーマットに変換しています。

 

 

APIで株価データを取得するコーディング

def get_stock_price():
params = {
"function": "TIME_SERIES_DAILY",
"symbol": STOCK_NAME,
"apikey": STOCK_API,
}
url = STOCK_ENDPOINT
r = requests.get(url, params=params)
data = r.json()["Time Series (Daily)"]
data_list = [value for (key,value) in data.items()]
today_data = float(data_list[0]["4. close"])
yesterday_data = float(data_list[1]["4. close"])

if today_data <= yesterday_data * 0.9:
print("OMG! why?")
get_stock_news()
else:
print("Keep it!")

 

url=をエンドポイントとしてリクエストし、paramsに必要事項を入力して

データをもらっています。

パラーメーターはAPIのドキュメントに書いてあります。

 

www.alphavantage.co

とりあえずrequiredだけやっとけば最低限の価格は取得できます。

取得したjsonを内包記述でリスト変換し、今日と機能の終値だけを取得します。

 

今回は今日の終値が昨日の終値より10%以上下げた時に、

ニュースを取得するという機能を実装しています。

 

ニュースを取得する機能の実装

def get_stock_news():

params = {
"q": company_name,
"from": today_news,
"apikey": NEWS_API,
"language": "en",
"searchIn": "title",
}
response = requests.get(NEWS_ENDPOINT, params=params)
response.raise_for_status()
data = response.json()
if data["articles"] == []:
print("no news...")
else:
article_source = data["articles"][0]["source"]["name"]
article_title = data["articles"][0]["title"]
article_url = data["articles"][0]["url"]
print(f"{article_source} : {article_title} - {article_url}")

get_stock_price()

 

こちらはNEWS APIを使います。

newsapi.org

 

同じようにドキュメントを見てパラメータをparamsにいれていきます。

入力例を見た方がわかりやすいかと思います。

sourcesではニュースサイトの指定もできるので、たとえばCNNだけから取得するということも可能。

 

取得がもしできなかったり空の場合は no news...と表示し、

あった場合は取得したjsonからメディア名・ニュースタイトル・リンク先を

アウトプットするようにしています。

 

 

クラッチにしてはちゃんと機能したものが作れたので気分がいいです笑

 

APIを使ったデータ解析になれてきたら、

yahooファイナンス日本株apiでfor で全部の株のデータ取得して

解析とかしてみたいな。

 

Gitはこちら。

 

github.com

 

 

pythonとphpの記述比較

pythonの基礎的な記述方法は覚えたつもりなので、

phpの方も少しさらってみると用途は違うものの少しアレンジすれば

phpもできるんじゃなかろうかと思い違いをまとめてみた。

javascriptも基本かじってきているのでそれも後日復習する予定。

 

変数の定義

python = 0
$php = 0;
PHPは最初に$をつけ、最後に";"をつける

 

記述方法

PHP<?php ?>の中に記述する

 

リストの記述

numbers = [1, 3, 5]
$numbers = array(1, 3, 5)

 

コンソールに出力する方法

print("hello")

 

phpは複数ある(めんどくさい)
echo "hello";
print_r($hello) 配列やオブジェクトの値を出力
print("hello") 1つだけ出力
var_damp($now)

 

ifの記述

if a > b or a = 4:
    b = 0
elif a < b and b = 3:
    c = 0
else:
    pass

 

<?php 
    if (a > b) || a = 4 {
        b = 0
    }
    else if (a < b) && b = 3 {
        c = 0
    }
?>
場合によってhtmlを差し込みたい場合は
<?php if (a > b): ?>
<p> 上の記述の時のみ表示する</p>
<?php else if (a < b): ?>
<p> 上の記述の時のみ表示する2</p>
<?php else: ?>
<p> 上の記述の時のみ表示する3</p>
<?php endif; ?>

 

whileの記述

a = 0
while a < 5 :
    a += 1
    print(a)

 

<?php
$a = 0;
while ($a < 5) {
    $a += 1;
    echo $a;
}
?>
phpはdo-while というwhileの使い方もある
<?php
do {
    $a = mt_rand(1,5);
    $b = mt_rand(1,5);
    $c = mt_rand(1,5);
    $abc = $a + $b + $c;
    if ($abc == 6) {
        echo "finish!\n";
        break;
    }
}
    while (TRUE);
    echo "抜け出せた3つの数字は{$a}、{$b}、{$c}";
?>

 

for文の記述

for i in range(5):
    print(f"{i}回")

 

<?php
    for ($i=1; $i< 6; $i++){
        echo "{$i}回\n";
    }
?>

 

ランダムな整数の出力方法

import random
random_num = random.randint(1, 30)
print(random_num)
hp
<?php
    $random_num = mt_rand(1, 30);
    echo $random_num
?>

 

for文の入れ子記述

 


for i in range(6):
    for j in range(6):
        print(f"{i}-{j} | ", end="")
    print("")

 

<?php
    for ($i=0; $i<=3; $i++) {
        for ($j=0; $j<=5; $j++){
            echo "{$i}-{$j}" . " | ";
        }
        echo PHP_EOL;
    }
?>

 

ユーザー定義関数

def sample(n):
    add = n * 2
    return add

print (sample(11))

 

<?php
    function sample ($n) {
        $add = $n * 2;
        return $add;
    }
    echo sample(10)
?>

 

処理の中断・break・continue

numbers = [5, 10, 15, 25, -10, 40, 5]
total = 0
total2 = 0
for i in numbers:
    if i > 0:
        total += i
    else:
        print("-があったので中断します")
        break
print(total)

 

<?php
    $numbers = array(5, 10, 15, 25, -10, 40, 5);
    $count = count($numbers);
    $total = 0;
    $total2 = 0;
    for ($i = 0; $i < $count; $i++){
        $value = $numbers[$i];
        if ($value < 0){
            echo "-がありましたが計算せず続けます\n";
            continue;
        }
        $total += $value;
    }
    echo $total;
    echo PHP_EOL;
    
    for ($i = 0; $i < $count; $i++){
        $value = $numbers[$i];
        if ($value < 0){
            echo "-があったので中断します\n";
            break;
        }
        $total2 += $value;
    }
    echo $total2;
?>

 

引数の個数を限定しない記述方法

python *argsを使う
def team(team_name, *members):
    print(team_name)
    print(members)

team("peach","angela","nick","mark")

 

php ...を使う
<?php
    function team($team_name, ...$members) {
        echo $team_name . "\n";
        print_r($members);
    }

    team("peach","angela","nick","mark")
?>

 

 

用途以外の大まかな違いは、

phpは毎回<?php ~ ?>をいれる

変数には$がつく

関数はpython = def , php = function

な感じですね。

 

pythonのコードがすっきりして書きやすいのを再確認しました。

変数とか毎回$つけんのめんどくさい…笑

 

DAY34 APIでデータを自動的に取得して毎回質問の違うクイズアプリを作る!

今回はかなり難しくてできなかったのでいい復習になった。

それぞれの機能をわけて1つのアプリとして作ります。

 

今回はDAY17でやったクイズアプリを、コンソールではなく

tkinterGUIを使ってより遊びやすくするアプリを作成します。

 

また質問もTRIVIA APIから毎回取得して

毎回違う問題を出すようにします。

 

opentdb.com

 

UIはこんな感じ。

 

 

 

APIからデータを取得するファイルを作成する

data.py

import requests

params = {
"amount": 10,
"category": 18,
"difficulty": "easy",
"type": "boolean"
}
response = requests.get("https://opentdb.com/api.php", params=params)
response.raise_for_status()
response = response.json()
quiz_list = response["results"]

今回のAPIはキーも要らず簡単なものなので、シンプルに。

requestsでapiのエンドポイントにアクセス。

raise_for_statusをすることでアクセスエラーが起きた時にどのエラーかを教えてくれるようにしています。

問題なかったらjson()でjsonフォーマットに。

quiz_listで余計な部分は飛ばして整形しています。

 

クイズ内容を質問・答えのセットとしたオブジェクトとして管理する

整形したjsonをアプリで出力しやすくするためにオブジェクト化します。

クイズモデルとして1つのクラスを作成します。

 

quiz_model.py

class Question:

def __init__(self, q_text, q_answer):
self.text = q_text
self.answer = q_answer

これはjsonの中にあるクイズ内容と答えをセットにするテンプレみたいなものを作っています。

 

main.py

from data import quiz_list
from quiz_model import Question

quiz_bank = []

for quiz in quiz_list:
q_text = quiz["question"]
q_ans = quiz["correct_answer"]
new_quiz = Question(q_text, q_ans)
quiz_bank.append(new_quiz)

quiz = QuizMachine(quiz_bank)

起動ファイルでjsonデータとモデルデータをインポートして、

for文で1つずつ質問と答えをとりだし、new_quizでオブジェクト化をして

quiz_bankのリストに入れていきます。

 

最後のQuizMachineでは整形したデータを取得して

クイズゲームを担当する機能で、次に実装していきます。

 

 

 

クイズを扱う機能のファイルを作成する

quiz_machine.py

import html


class QuizMachine:

def __init__(self, quiz_bank):
self.question_number = 0
self.score = 0
self.quiz_bank = quiz_bank
self.current_question = None

def still_has_questions(self):
return self.question_number < len(self.quiz_bank)

def next_question(self):
self.current_question = self.quiz_bank[self.question_number]
self.question_number += 1
q_text = html.unescape(self.current_question.text)
return f"Q.{self.question_number}: {q_text}"

def check_answer(self, user_answer):
correct_answer = self.current_question.answer
if user_answer.lower() == correct_answer.lower():
self.score += 1
return True
else:
return False

QuizMachineというクラスを作ります。

 

整形したデータはmain.pyでさらに出題しやすいように抽出されたデータを作るのですが、

そのデータからクイズを出す機能を作ります。

主に

  • データを取得する
  • スコアの設定
  • データがある分次々と出題する
  • 正解かを判断する

です。

上から説明すると、init部分では初期状態として、スコアと取得したデータ、今何問目かを初期化します。selfの次にquiz_bankがある通り、このクラスを使う時は()にquiz_bankを入れることで機能します。

 

still_has_questionは取得したデータの数を数えて、その分だけ数を出力するもの。

出力されなかったらクイズ終了の表示をUIのファイルがする予定。

 

next_questionでは初期値のquestion_numberのリストの内容を取得してから1を足して

質問内容を出力しています。

これをすることでリストとしては0番目の内容が見た目は1番目として見せることができます。

 

htmlをインポートしているのは、質問の中には &quot;+= 1&quot のように

ウェブサイト上で表記するための記号がある場合があります。

それをpython上でもきちんと表示されるように変換するためのファンクション = unescapeを使うためにインポートしています。

 

UIを作成する

 

design.py

 

from tkinter import *
from quiz_machine import QuizMachine

THEME_COLOR = "#efefef"


class QuizUI:

def __init__(self, quiz_brain: QuizMachine):
self.quiz = quiz_brain

self.window = Tk()
self.window.title("Quizzler")
self.window.config(padx=20, pady=20, bg=THEME_COLOR)

self.score_label = Label(text="Score: 0", fg="#333", bg=THEME_COLOR)
self.score_label.grid(row=0, column=0, columnspan=2)

self.canvas = Canvas(width=300, height=250, bg="white")
self.question_text = self.canvas.create_text(
150,
125,
width=280,
text="Some Question Text",
fill="#333",
font=("Arial", 20, "italic")
)
self.canvas.grid(row=1, column=0, columnspan=2, pady=50)

true_image = PhotoImage(file="images/maru.png")
self.true_button = Button(image=true_image, highlightthickness=0, command=self.true_pressed)
self.true_button.grid(row=2, column=0)

false_image = PhotoImage(file="images/batsu.png")
self.false_button = Button(image=false_image, highlightthickness=0, command=self.false_pressed)
self.false_button.grid(row=2, column=1)

self.get_next_question()

self.window.mainloop()

 

UIにQuizMachineをインポートすることでUIファイルでQuizMachineの機能が使えるようになります。

innitに(self, quiz_brain: QuizMachine)を入れて、

self.quiz = quiz_brainを入れることで継承ができます。

 

あとはtkinterの今まで使った機能と同じ感じ。

丸ボタンを押すとtrue_pressed、バツボタンでfalse_pressedという機能を発火します。

 

一通りのUIができたので、最後にクイズを取得する機能 = get_next_question()を入れて

innit部分は終了。

 

次に、UI上の変化として

  • クイズが残っていればクイズを表示する
  • 丸ボタンを押すとTrue、バツボタンを押すとFalseを返す
  • 答えと合っていればスコアを+1にする
  • 背景を正解の色(丸なら青、バツなら赤)に変化する。

を実装していきます。

 

design.py


def get_next_question(self):
self.canvas.config(bg="white")
if self.quiz.still_has_questions():
self.score_label.config(text=f"Score: {self.quiz.score}")
q_text = self.quiz.next_question()
self.canvas.itemconfig(self.question_text, text=q_text, fill="#333")
else:
self.score_label.config(text=f"Final Score: {self.quiz.score}")
self.canvas.itemconfig(self.question_text, text=f"Quiz is End!\nYour Score is {self.quiz.score}", fill="#333")
self.true_button.config(state="disabled")
self.false_button.config(state="disabled")

def true_pressed(self):
self.give_feedback(self.quiz.check_answer("True"))

def false_pressed(self):
is_right = self.quiz.check_answer("False")
self.give_feedback(is_right)

def give_feedback(self, is_right):
if is_right:
self.canvas.config(bg="blue")
self.canvas.itemconfig(self.question_text, fill="#fff")
else:
self.canvas.config(bg="red")
self.canvas.itemconfig(self.question_text, fill="#fff")
self.window.after(1000, self.get_next_question)

 

self.quiz.機能 は、QuizMachineに記述したdefの機能を読み込ませています。

一番最初に背景を白に設定しているのは答えを押した後に背景が青か赤になってしまっているのでそれをクリアするためです。

最初にstill_has_questionsを使うことでクイズがあるかどうかを判断しています。

そこからクイズがあればテキストをnext_question()を起動して

return として出力している Q1: クイズ のテキストをq_text の変数に格納し、

クイズ表示部分に表示をしています。

 

 

true_pressed, false_pressedはcheck_answer()を起動して

True,またはFalseを入れることで

correct_answer と同じかどうかをチェックしてあっていたらスコア+1でTrueを返す、

間違っていたらFalseを返します。

という機能にしています。

 

自分の答えが合っていると+1をしてTrueを返す quiz.check_answerですが、

Falseの時だけ is_rightという機能が付いています。

それは下のgive_feedbackの部分で正解だったら青背景、間違っていたら赤背景にするためです。

 

 

UI部分もできたのでmain.pyの最後にUIのクラスを追加して出来上がり。

 

ファイルを行き交う継承とか結構ややこしい。。。

 

Gitはこちら

github.com