nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Pythonでファイルからデータを読み込んで、リストに格納する

Pythonでそのままリスト表示

csv形式のテキストファイルを読み込んで、リストに格納して表示するプログラムです。 テキストファイルは以下を用います。

データファイル

Ichiro,100
Jiro,95

ただ単純にリスト表示すると以下のように、バックスラッシュが入っていたり、要素が分けられていない問題が発生します。

実行イメージ

f:id:nprogram:20180926173531p:plain

コード

score_list = []

score_list_file = open("score")

for score in score_list_file:
    score_list.append(score)

score_list_file.close()

print(score_list)

整形してリスト表示する

rstrip関数でバックスラッシュを削除して、split関数でカンマで要素を分割します。

また、今回は、数字があるため、score_listのリストに要素を追加する際は、数字のパラメータについては、int型でキャストしました。

実行イメージ

f:id:nprogram:20180926172340p:plain

コード

score_list = []

score_list_file = open("score")

for score in score_list_file:
    score = score.rstrip()
    score = score.split(",")
    score_list.append([score[0], int(score[1])])

score_list_file.close()

print(score_list)

株価データをプロットして、CSVファイルを出力する方法

はじめに

jsmを使えば、日本の銘柄の株価を簡単に取得できます。

パッケージ・バージョン

jsm : 0.19

コード

import datetime as dt
from pandas import DataFrame
import jsm
import pandas_datareader.data as web
import matplotlib.pyplot as plt

def jpstock(code, start_date, end_date):
    year, month, day = start_date.split("-")
    start = dt.date(int(year), int(month), int(day))
    year, month, day = end_date.split("-")
    end = dt.date(int(year), int(month), int(day))

    print('CSVを出力中...')
    q = jsm.Quotes()
    target = q.get_historical_prices(code, jsm.DAILY, start_date=start, end_date=end)

    date = [data.date for data in target]
    open = [data.open for data in target]
    high = [data.high for data in target]
    low = [data.low for data in target]
    close = [data.close for data in target]
    volume = [data.volume for data in target]
    adj_close = [data._adj_close for data in target]

    Date = date[::-1]
    Open = open[::-1]
    High = high[::-1]
    Low = low[::-1]
    Close = close[::-1]
    Adj = adj_close[::-1]
    Vol = volume[::-1]

    cdf = DataFrame(index=Date)
    cdf.index.name = "Date"
    cdf["Open"] = Open
    cdf["High"] = High
    cdf["Low"] = Low
    cdf["Close"] = Close
    cdf["Adj Close"] = Adj
    cdf["Volume"] = Vol

    cdf.to_csv(code + '.csv')
    print(code + '.csvを出力しました.')

    print('株価データをプロット中...')
    df = DataFrame(index=Date)
    df['Adj Close'] = Adj

    return df

code='8411'
start_date =  '2018-01-01 '
end_date =  '2018-09-01 '

try:
    jstock = jpstock(code, start_date, end_date)
    jstock['Adj Close'].plot()
    plt.show()
except:
     print('データの取得中にエラーが発生しました.')

銘柄コード(8411)の2018年1月1日から9月1日までに株価の推移

f:id:nprogram:20180921041051p:plain

ワーニングの対処

jsmでユーザーワーニングが発生しますが、実行には問題ありません。ワーニングに対処する場合は、以下のように修正してください。

  • 修正前 [util.py]
soup = BeautifulSoup(html)
  • 修正後 [util.py]
soup = BeautifulSoup(html, 'html.parser')

github.com

GUI版も提供されていました

参考記事のほうで、GUI版も提供されていました。 pyqt5というパッケージをインストールする必要があります。 pip install pyqt5

参考記事

以下の記事を参考にしました。

qiita.com

Pythonで株価のスクレイピングをしてみました

はじめに

Pythonで株価を取得してみました。

自分で、スクレイピングのコードを書かなくても、世の中には、その機能を提供するパッケージが複数あるようです。

  • Quandl
  • pandas_datareader
  • jsm

ここでは、pandas_datareaderを用いて、株価を取得してみたいと思います。

コード

import pandas_datareader.data as web
from datetime import datetime

start = datetime(2000, 1, 1)
end = datetime(2018, 9, 17)
df = web.DataReader('8411.JP', 'stooq')
print(df.head())

上記のコードを実行すると、以下の出力結果が得られます。

<コンソール出力結果>

             Open   High    Low  Close     Volume
Date
2018-09-13  192.0  194.3  191.7  193.3   87685600
2018-09-12  192.4  192.5  191.2  192.0  102462000
2018-09-11  192.7  193.0  192.0  192.7   70223700
2018-09-10  192.3  193.7  191.7  192.9   75642400
2018-09-07  192.2  192.6  190.8  192.0  112240600

Pythonを用いて、簡単に株価を取得できました。

自力でも、スクレイピングができるように引き続き調査します。

Pythonで簡単なグラフを書いてみました

はじめに

matplotlibを使うことで、簡単にグラフを書くことができます。

環境

  • IDE : Visual Studio Code (Version 1.27.2)
  • OS : Windows 10 (Version 1803)

パッケージ・モジュールのバージョン

  • Python 3.7.0
  • matplotlib 2.2.3

簡単グラフを書く

コード1

import matplotlib.pyplot as plt
plt.plot([1,2,3,4])
plt.show()

表示イメージ1

f:id:nprogram:20180917135422p:plain:w300

日経平均株価の推移を表示 (2018年1月1日 - 2018年9月17日)

コード2

import matplotlib.pyplot as plt

import datetime
import pandas_datareader.data as web

start = datetime.datetime(2018, 1, 1)
end   = datetime.datetime(2018, 9, 17)

nikkei225 = web.DataReader("NIKKEI225", "fred", start, end) 
nikkei225.plot()
plt.show()

表示イメージ2

f:id:nprogram:20180917141040p:plain:w300

補足

Pythonのバージョンやパッケージのバージョンは、以下のコマンドを使用してください。

  • Python バージョン確認方法 -- python --version
  • パッケージのバージョン確認 -- pip list

整数値をアスキーコードに変換する方法 (_itoa_s関数使用)

はじめに

例えば、124や0x2Fなどの整数値をアスキーコードに変換しようとする場合は、_itoa_sを使うと簡単に変換できます。
関数仕様については、MSDNのドキュメントをご確認ください。

errno_t _itoa_s(  
   int value,  
   char *buffer,  
   size_t sizeInCharacters,  
   int radix   
);  

環境

  • Visual Studio Community 2017
  • C++コンソールアプリケーション

コード

コードを以下に記載します。

#include "stdafx.h"
#include <stdlib.h>  
#include <string.h> 

int main()
{
        int i;
        char buffer[33] = { 0 };


        printf("Enter a number: ");
        scanf_s("%X", &i);

        // 入力した値を、基数(10)に従ってアスキーコードに変換
        _itoa_s(i, buffer, 10);
        printf("decimal: %s\n", buffer);
        printf("buffer length: %d\n", static_cast<int>(strlen(buffer)));

        // 入力した値を、基数(16)に従ってアスキーコードに変換
        _itoa_s(i, buffer, 16);
        printf("hexadecimal: %s\n", buffer);
        printf("buffer length: %d\n", static_cast<int>(strlen(buffer)));

        // 入力した値を、基数(2)に従ってアスキーコードに変換
        _itoa_s(i, buffer, 2);
        printf("binary: %s\n", buffer);
        printf("buffer length: %d\n", static_cast<int>(strlen(buffer)));


        // バッファクリアテスト
        printf("\r\n\r\n---- buffer zero clear ---- \r\n\r\n");
        memset(buffer, 0, sizeof(buffer));

        for (int i = 0; i < sizeof(buffer); i++)
        {
            printf("buffer[%d] = %d\r\n", i, buffer[i]);
        }

        // _itoa_s関数テスト
        printf("\r\n\r\n---- execute _itoa_s ---- \r\n\r\n");
        int targetNum = 0x2F;
        _itoa_s(targetNum, buffer, 16);
        printf("hexadecimal: %s\n", buffer);
        printf("buffer length: %d\n", strlen(buffer));

        for (int i = 0; i < sizeof(buffer); i++)
        {
            printf("buffer[%d] = %d\r\n", i , buffer[i]);
        }

        system("pause");
        return 0;
}

テスト結果

基数に従って、アスキーコードに変換されていることがわかります。

Enter a number: 100
decimal: 256
buffer length: 3
hexadecimal: 100
buffer length: 3
binary: 100000000
buffer length: 9


---- buffer zero clear ----

buffer[0] = 0
buffer[1] = 0
buffer[2] = 0
buffer[3] = 0
buffer[4] = 0
buffer[5] = 0
buffer[6] = 0
buffer[7] = 0
buffer[8] = 0
buffer[9] = 0
buffer[10] = 0
buffer[11] = 0
buffer[12] = 0
buffer[13] = 0
buffer[14] = 0
buffer[15] = 0
buffer[16] = 0
buffer[17] = 0
buffer[18] = 0
buffer[19] = 0
buffer[20] = 0
buffer[21] = 0
buffer[22] = 0
buffer[23] = 0
buffer[24] = 0
buffer[25] = 0
buffer[26] = 0
buffer[27] = 0
buffer[28] = 0
buffer[29] = 0
buffer[30] = 0
buffer[31] = 0
buffer[32] = 0


---- execute _itoa_s ----

hexadecimal: 2f
buffer length: 2
buffer[0] = 50
buffer[1] = 102
buffer[2] = 0
buffer[3] = -2
buffer[4] = -2
buffer[5] = -2
buffer[6] = -2
buffer[7] = -2
buffer[8] = -2
buffer[9] = -2
buffer[10] = -2
buffer[11] = -2
buffer[12] = -2
buffer[13] = -2
buffer[14] = -2
buffer[15] = -2
buffer[16] = -2
buffer[17] = -2
buffer[18] = -2
buffer[19] = -2
buffer[20] = -2
buffer[21] = -2
buffer[22] = -2
buffer[23] = -2
buffer[24] = -2
buffer[25] = -2
buffer[26] = -2
buffer[27] = -2
buffer[28] = -2
buffer[29] = -2
buffer[30] = -2
buffer[31] = -2
buffer[32] = -2
続行するには何かキーを押してください . . .

16進数の値をアスキーコードに変換して、動的配列(std::vector)に格納する。その後、std::vectorをchar配列に変換

#include "stdafx.h"
#include <iostream>
#include <vector>

int main()
{
        std::vector<char> v;

        // 16進数の値をアスキーコードに変換して、動的配列(std::vector<char>)に格納する
        for (int i = 0; i < 16; i++)
        {
            char buffer[3] = { 0 };

            _itoa_s(i, buffer, 16);

            for (int j = 0; j < strlen(buffer); j++)
            {
                v.push_back(buffer[j]);
            }

        }

        printf("\r\n\r\n---- result ---- \r\n\r\n");
        for (int x : v) {
            std::cout << x << std::endl;
        }

        printf("\r\n\r\n---- result(16進数表示) ---- \r\n\r\n");
        for (int x : v) {
            std::cout << "0x" << std::hex << x << std::endl;
        }

        // std::vector<char>をchar配列に変換
        printf("\r\n\r\n---- std::vector<char>をchar配列に変換 ---- \r\n\r\n");
        int nSize = v.size();

        char *strResult = new char[nSize];

        memcpy_s(strResult, nSize, v.data(), nSize);

        for (int i = 0; i < nSize; i++)
        {
            printf("%02X(code : %c)\r\n", *(strResult + i), *(strResult + i));
        }

        system("pause");

        delete[] strResult;

        return 0;
}
---- result ----

48
49
50
51
52
53
54
55
56
57
97
98
99
100
101
102


---- result(16進数表示) ----

0x30
0x31
0x32
0x33
0x34
0x35
0x36
0x37
0x38
0x39
0x61
0x62
0x63
0x64
0x65
0x66


---- std::vector<char>からchar配列に変換 ----

30(code : 0)
31(code : 1)
32(code : 2)
33(code : 3)
34(code : 4)
35(code : 5)
36(code : 6)
37(code : 7)
38(code : 8)
39(code : 9)
61(code : a)
62(code : b)
63(code : c)
64(code : d)
65(code : e)
66(code : f)

WPF DataTemplateの学習

はじめに

WPF DataTemplateの学習は、以下のMicrosoftのサンプル(DataTemplatingIntro)とドキュメントを合わせて確認するのがいいと思います。

github.com

まずは、DataTemplateを使用していないイメージ1までのコードです。

イメージ1

f:id:nprogram:20180725205213p:plain

コード

[MainWindow.xaml]

<Window x:Class="WpfApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <local:Tasks x:Key="myTodoList"/>
    </Window.Resources>
    <StackPanel>
        <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
        <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
    </StackPanel>
</Window>

[MainWindow.xaml.cs]

using System.Collections.ObjectModel;
using System.Windows;


namespace WpfApp2
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class Tasks : ObservableCollection<Task>
    {
        public Tasks()
        {
            Add(new Task("Groceries", "Pick up Groceries and Detergent", 2, TaskType.Home));
            Add(new Task("Laundry", "Do my Laundry", 2, TaskType.Home));
            Add(new Task("Email", "Email clients", 1, TaskType.Work));
            Add(new Task("Clean", "Clean my office", 3, TaskType.Work));
            Add(new Task("Dinner", "Get ready for family reunion", 1, TaskType.Home));
            Add(new Task("Proposals", "Review new budget proposals", 2, TaskType.Work));
        }
    }

    public class Task
    {
        public string TaskName { get; set; }
        public string Description { get; set; }
        public int Priority { get; set; }
        public TaskType TaskType { get; set; }

        public Task(string someTaskName, string someDescription, int somePriority, TaskType someTaskType)
        {
            this.TaskName = someTaskName;
            this.Description = someDescription;
            this.Priority = somePriority;
            this.TaskType = someTaskType;
        }
    }

    public enum TaskType
    {
        Home,
        Work
    }
}

DataTemplateを定義する

ここで、ListBoxに、DataTemplateを使用することで、ListBoxの各Taskクラスのプロパティの値をListBoxに表示できるようになりました。

イメージ2

f:id:nprogram:20180725210310p:plain

[MainWindow.xamlの一部]

        <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Path=TaskName}" />
                        <TextBlock Text="{Binding Path=Description}"/>
                        <TextBlock Text="{Binding Path=Priority}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

DataTemplateをWindows.Resourcesに登録

これだと、再利用性が低いので、再利用できるように、Windows.Resourcesに登録します。

[MainWindow.xamlの一部]

    <Window.Resources>
        <local:Tasks x:Key="myTodoList"/>

        <DataTemplate x:Key="myTaskTemplate">
            <StackPanel>
                <TextBlock Text="{Binding Path=TaskName}" />
                <TextBlock Text="{Binding Path=Description}"/>
                <TextBlock Text="{Binding Path=Priority}"/>
            </StackPanel>
        </DataTemplate>
        
    </Window.Resources>
    <StackPanel>
        <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
        <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"
             ItemTemplate="{StaticResource myTaskTemplate}"/>
    </StackPanel>

DataType プロパティを使用する

また、以下のように、DataTypeを使用しても、同じように表示可能です。

    <Window.Resources>
        <local:Tasks x:Key="myTodoList"/>

        <DataTemplate DataType="{x:Type local:Task}">
            <StackPanel>
                <TextBlock Text="{Binding Path=TaskName}" />
                <TextBlock Text="{Binding Path=Description}"/>
                <TextBlock Text="{Binding Path=Priority}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    <StackPanel>
        <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
        <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
    </StackPanel>

DataTemplateにさらに要素を追加する

もっと、美しく表示させてみます。

イメージ3

f:id:nprogram:20180725225653p:plain

[MainWindow.xamlの一部]

    <Window.Resources>
        <local:Tasks x:Key="myTodoList"/>

        <DataTemplate x:Key="myTaskTemplate">
            <Border Name="border" BorderBrush="Aqua" BorderThickness="1"
          Padding="5" Margin="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    
    <StackPanel>
        <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
        <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"
             ItemTemplate="{StaticResource myTaskTemplate}"/>
    </StackPanel>

DataTriggerを使ってみる

イメージ4

f:id:nprogram:20180725230736p:plain

[MainWindow.xamlの一部]

        <DataTemplate x:Key="myTaskTemplate">
            <Border Name="border" BorderBrush="Aqua" BorderThickness="1"
          Padding="5" Margin="5">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
                </Grid>
            </Border>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=TaskType}">
                    <DataTrigger.Value>
                        <local:TaskType>Home</local:TaskType>
                    </DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </Window.Resources>

データオブジェクトのプロパティに基づくDataTemplateの選択

イメージ5

f:id:nprogram:20180726075325p:plain

コード (MainWindow.xaml.csに追加)

    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;

            if (element != null && item != null && item is Task)
            {
                Task taskitem = item as Task;

                if (taskitem.Priority == 1)
                    return
                        element.FindResource("importantTaskTemplate") as DataTemplate;
                else
                    return
                        element.FindResource("myTaskTemplate") as DataTemplate;
            }

            return null;
        }
    }

コード (MainWindow.xaml)に追加

    <Window.Resources>
        <local:Tasks x:Key="myTodoList"/>

        <local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>

        <DataTemplate x:Key="myTaskTemplate">
        //// 省略 ////
        </DataTemplate>
        
        <DataTemplate x:Key="importantTaskTemplate">
            <DataTemplate.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="FontSize" Value="20"/>
                </Style>
            </DataTemplate.Resources>
            <Border Name="border" BorderBrush="Red" BorderThickness="1"
          Padding="5" Margin="5">
                <DockPanel HorizontalAlignment="Center">
                    <TextBlock Text="{Binding Path=Description}" />
                    <TextBlock>!</TextBlock>
                </DockPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    
    <StackPanel>
        <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
        <ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
         HorizontalContentAlignment="Stretch"/>
    </StackPanel>

トラブルシューティング

  • xamlの記載において、<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>で、名前空間に存在するにもかかわらず、TaskListDataTemplateSelectorが見つかりません。と指摘される場合があります。その場合は、Visual Studioを再起動すると、直る場合があります。

Python文法学習 (関数、クロージャー、ジェネレーター、ラムダ式)

はじめに

Pythonを学ぶため、Pythonの基礎を本ページに記載します。

関数

関数の引数は、型がないので、注意が必要です。attack関数の引数は、文字列なのです。

パラメーター(引数)が1つの場合

def add_sound(attack):
    for i in range(5):                  # 5回繰り返す
        print(attack, '-->パコーン')    # パラメーターの値に擬音を付けて表示
    return attack


add_sound('スマッシュ')

パラメーター(引数)が複数の場合

def calculate_add(a, b):
    result_calculate = a + b
    return result_calculate


result = calculate_add(3, 5)

print(result)

デフォルトパラメーターも使えます

def calculate_add(a=1, b=2):
    result_calculate = a + b
    return result_calculate


#result = calculate_add(3, 5)
result = calculate_add()
print(result)

可変長パラメーターも使えます

# パラメーター名の前にアスタリスク(*)を付けると可変長パラメーターとなります
def sequence_sound(*args):
    for a in(args):
        print(a)
    print(args)


sequence_sound('ポーン', 'パコーン', 'スコーン')

[実行イメージ]

ポーン
パコーン
スコーン
('ポーン', 'パコーン', 'スコーン')

キーと値がセットになったパラメーター

「** パラメーター名」と書くとそのパラメーターは辞書型になります。

# パラメーター名の前にアスタリスク(*)を付けると可変長パラメーターとなります
def attacks(**kwargs):
    print(kwargs)


attacks(volley='ポーン', smash='パコーン')

パラメーターで関数を取得して、これを実行する

# パラメーターで関数を取得し、これを実行する関数を定義
# 関数をパラメーターで取得したり、戻り値として返す関数を高階関数と呼ぶ

def attack_sound(a, s):
    print(a, '-->', s)


def run_something(func, arg1, arg2):
    func(arg1, arg2)


run_something(attack_sound, 'ドロップショット', 'ポワーン')

# ドロップショット --> ポワーン

関数内関数の定義

関数の中で関数を定義できます。複雑な処理を内部の関数に任せることで、コードの重複を避けることができます。

EX. 整数値の例

def outer(a, b):
    def inner(c, d):
        return c + d
    return inner(a, b)


result = outer(1, 5)

print(result)

print(outer(1, 5))

関数内関数の定義2 (文字列を扱う場合)

関数のパラメーター名strokeと関数内関数のパラメーター名sが異なるようにします。 同一の場合は、コンパイラーによっては、警告を受けます。

def add_sound(stroke):

    # 関数内関数
    def inner(s):
        return s + ' --> ' + 'ぱっこーん'

    return inner(stroke)     # 関数内関数の結果を返す


result = add_sound('フォアハンドストローク')

print(result)

# フォアハンドストローク --> ぱっこーん

クロージャー

クロージャーとは、引数をセットして、関数を呼び出すコードを作っておいて、それをあとで実行できるようにするもの 関数を呼び出すパターンがあらかじめわかっているなら、それを記録しておいて、必要なときに実行するといった使い方ができます 次は、先の関数内関数をクロージャーにしたものです。

def add_sound(stroke):

    # クロージャーを定義する前に、呼び出すとエラーになるため、注意
    # inner()

    # クロージャー
    def inner():
        return stroke + ' --> ' + 'ぱっこーん'

    print(inner())

    return inner     # 関数内関数の結果を返す


result = add_sound('フォアハンドストローク')

print(result())

# フォアハンドストローク --> ぱっこーん
# フォアハンドストローク --> ぱっこーん

関数内関数とクロージャーは以下の点が異なります。 inner()にはパラメーターがなく、代わりに外側の関数のstrokeパラメーターを直接使う add_sound()は、inner()関数の処理結果を返すのではなく、関数名(関数オブジェクト)を返す。

inner()関数はadd_sound()に渡されたstrokeにアクセスすることができ、これを覚えておくことができます。 これがクロージャーの重要なポイントです。

一方、add_sound()関数は、戻り値として、inner()を関数オブジェクトとして、返します。 strokeに渡す値を引数にしてadd_sound()を実行すれば、storkeの値を保持したinner()関数のオブジェクトが返されます。 この関数オブジェクトがすなわちクロージャーです。

def add_sound(stroke):

    # クロージャーを定義する前に、呼び出すとエラーになるため、注意
    # inner()

    # クロージャー
    def inner():
        return stroke + ' --> ' + 'ぱっこーん'

    return inner     # 関数内関数の結果を返す


a = add_sound('フォアハンドストローク')
b = add_sound('バックハンドストローク')

print(a())
print(b())


# フォアハンドストローク --> ぱっこーん
# バックハンドストローク --> ぱっこーん

クロージャーaとbは、自分たちが作られたときに使われていたstrokeの内容を覚えています。あとは、実行したいタイミングでクロージャーを呼び出す。

ラムダ式

高階関数を用いた実現例は以下のようになります。

# 高階関数 (パラメータ
def edit_sounds(sounds, func):
    for sound in sounds:
        print(func(sound))


# パラメーターで取得した値の末尾に感嘆符を追加する関数
def impact(sound):
    return sound + '!!!'


pattern = ['ポーン', 'ぱっこーん', 'ビシ']

edit_sounds(pattern, impact)

# 実行イメージ
#  ポーン!!!
# ぱっこーん!!!
# ビシ!!!

次に上をラムダ式で表現します。 ラムダ式を用いることで、impact関数が必要無くなりました。 ラムダ式は、名前のない処理部だけの関数であるため、無名関数と呼ばれる。

# 高階関数 (パラメータ
def edit_sounds(sounds, func):
    for sound in sounds:
        print(func(sound))


# パラメーターで取得した値の末尾に感嘆符を追加する関数
def impact(sound):
    return sound + '!!!'


pattern = ['ポーン', 'ぱっこーん', 'ビシ']

edit_sounds(pattern, lambda sound: sound + '!!!')

# 実行イメージ
#  ポーン!!!
# ぱっこーん!!!
# ビシ!!!

ジェネレーター

ジェネレーターとは、Pythonのシーケンスを作成するオブジェクトのこと。ジェネレーターオブジェクトでは、戻り値をreturnではなくyieldで返す関数で生成することができます。反復処理のrange()もジェネレーター関数。

ジェネレーターでは、反復処理のたびに、最後に呼び出されたときに、シーケンスのどこを指していたか覚えていて、次の値を返します。

一方、通常の関数は、以前の呼び出しについて何も覚えていません。

def generate(str):
    for a in str:
        yield '[' + a + ']'


gen = generate('ぱっこーん!')

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

print(next(gen))

# print(next(gen)) 文字列以上に処理を行うとするとエラーになるため、注意

# 実行イメージ
# [ぱ]
# [っ]
# [こ]
# [ー]
# [ん]
# [!]

イテレート(反復処理)が可能なので、forステートメントを使ったほうが簡単です。

def generate(str):
    for a in str:
        yield '[' + a + ']'


gen = generate('ぱっこーん!')

for s in gen:
    print(s)

# 実行イメージ
# [ぱ]
# [っ]
# [こ]
# [ー]
# [ん]
# [!]