かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Python3 Classのメモ

classキーワードに続けて大文字から始まるクラス名を記述してクラスを作成する
クラスからインスタンスを作成する際はnewなどのキーワードは不要
コンストラクタは__init__()で定義する

class Player:
  # constructor
  def __init__(self, name = 'Human'):
    self.name = name

  def say_hello(self, message = 'Yo'):
    print( f'{self.name} says "{message}"' )

player1 = Player('Dog')
player1.say_hello('Woof')
# => Dog says "Woof"

private / public

private変数・privateメソッドは変数名・メソッド名の前に__をつける

class Spam:
  __tax = 0.08
  value = 100

  # constructor
  def __init__(self, value = 100):
    self.value = value

  def __tax_amount(self, val):
    print( f'tax is {self.__tax}' )
    return int( round(val * self.__tax, 0) )

  def buy(self, num):
    amount = self.value * num
    return amount + self.__tax_amount(amount)

spam = Spam()
print( spam.buy(5) )
# => tax is 0.08
# => 540

spam.value = 300
print( spam.buy(5) )
# => tax is 0.08
# => 1620

# プライベート変数に外部からアクセス
print(spam.__tax)
# エラー
# AttributeError: 'Spam' object has no attribute '__tax'

# プライベートメソッドを外部から呼び出し
spam.__tax_amount(100)
# エラー
# AttributeError 'Spam' object has no attribute '__tax_amount'

# プライベート変数の置換え
#  => エラーにならないが無視される
spam.__tax = 0.1
print( spam.buy(5) )
# => tax is 0.08
# => 1620

プレイベート変数にアクセスしようとしたらエラーになったのですが、=で値を置き換えようとしたらエラーにならず無視されました。エラーにならないの理由がちょっと解りませんでした...

継承

クラス名に続けて()で継承元のクラスを指定する

class Ham:
  name = 'Ham'
  def say_name(self):
    print( self.name )

  def say(self):
    print('ham')

class Spam(Ham):
  def __init__(self):
    self.name = 'Spam'

  def say(self):
    self.say_name()

spam = Spam()
spam.say()
# => Spam
多重継承 (multiple inheritance)

継承乱発はあまり良くないと思うけど、多重継承もできるみたいなので試してみました。
継承元の指定を,区切りで多重継承ができるようです。

class A:
  def func(self):
    print('class A')

class B:
  def func(self):
    print('class B')

class C(B, A):
  pass

c = C()
c.func()
# => class B

多重継承した場合、メソッドが被っていると継承元として先に記述した方のメソッドが優先されるようです。


[参考]

スラスラ読める Pythonふりがなプログラミング (ふりがなプログラミングシリーズ)

スラスラ読める Pythonふりがなプログラミング (ふりがなプログラミングシリーズ)

Python 後で使う用に空の変数だけ作っておきたい。(変数宣言だけしたい)

なんとなくPythonJavaScriptに似てる気がしてきてる今日このごろ。

後のループとかで使うために関数の先頭とかで空の変数だけ作っておきたい(関数宣言だけしたい)ってのに少しハマったのでメモ。

varみたいな宣言文がPythonには無いので、変数だけ作ってみたらエラーになりました。

my_var
# => NameError: name 'my_var' is not defined

Pythonの変数は代入しないと作成されない。

Pythonの変数は型もなく、値を設定(代入)することで変数が作成されるという仕組みなようです。
つまり、何かしら代入しないと、その変数を探して「そんな名前の変数はない」とエラーを出すってことみたいです。

何かしら値を与えればOK

my_var = None

JavaScriptの変数巻き上げの癖で関数とかの先頭で変数定義したくなるので、同じような書き方しててハマってしましました。 先に使われる変数がある方が個人的には見通しが良いように感じるのですが、空文字''ではなく、Noneで変数を作成してもメモリ効率とかの面では良くないのでしょうか?
まぁ見通しが悪くなるくらい大きな関数作るなって話ですよね。。。


[参考]

変数人間 (ハヤカワ文庫SF)

変数人間 (ハヤカワ文庫SF)

Python3 文字列中に変数展開したい

1. %演算子

変換指定子 フォーマット型
%f 浮動小数点数
%d 整数
%s 文字列
%x 16進数
%o 8進数
%% %
print( 'Hello %s!' % 'World' )
# => Hello World
area = 3.14 * 5 * 5
print( '円の面積は%f平方cm' % area )
# => 円の面積は78.500000平方cm
cat = 3
dog = 6
print( 'cats are %d, dogs are %f' % (cat, dog) )
# => cats are 3, dogs are 6.000000

2. str.format()

str.format(*args, **kwargs)
置換フィールドは位置引数のインデックスナンバー、または、キーワード引数の名前を含みます。返り値は、それぞれの置換フィールドが対応する引数の文字列値で置換された文字列のコピーです。
出典: str.format - 4. 組み込み型 — Python 3.6.5 ドキュメント

引数の順序で指定

'{0}年{1}月{2}日'.format(2018, 8, 15)
# => 2018年8月15日

キーワードで指定

'{year}年{month}月{day}日'.format(year=2018, month=8, day=15)
# => 2018年8月15日

リスト・辞書

リスト

l = ['Mika', 'boss', 18]
'{0[0]}-{0[1]}-{0[2]}'.format(l)
# => Mika-boss-18

辞書

d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
'{0[name]}-{0[role]}-{0[age]}'.format(d)
# => Aki-gunner-17

※ リストも辞書もformat()引数の位置を指定する必要がある。下記のような記述はエラーになる

l = ['Mika', 'boss', 18]
'{[0]}-{[1]}-{[2]}'.format(l)
# => エラー

位置引数・キーワード引数でリスト・辞書を展開して渡す

リスト => 位置引数*

l = ['Mika', 'boss', 18]
'{0}: {1}-{2}-{3}'.format('Keizoku', *l)
# => Keizoku: Mika-boss-18

辞書 => キーワード引数**

d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
'{0}: {name}-{role}-{age}'.format('Keizoku' ,**d)
# => Keizoku: Aki-gunner-17

3. f-string (formatted string literal)

バージョン 3.6 で追加.
フォーマット済み文字列リテラル( formatted string literal )または f-string は、接頭辞 'f' または 'F' の付いた文字列リテラルです。これらの文字列には、波括弧 {} で区切られた式である置換フィールドを含めることができます。他の文字列リテラルの場合は内容が常に一定で変わることが無いのに対して、フォーマット済み文字列リテラルは実行時に式として評価されます。
出典: 2.4.3. フォーマット済み文字列リテラル

val = 'Wold'
f'Hello {val}!'
# => Hello Wold!

# リスト
l = ['Mika', 'boss', 18]
f'{l[0]}-{l[1]}-{l[2]}'
# => Mika-boss-18

# 辞書
d = {'name': 'Aki', 'role': 'gunner', 'age': 17}
f'{d["name"]}-{d["role"]}-{d["age"]}'
# => Aki-gunner-17

f'{d[name]}-{d[role]}-{d[age]}'
# => NameError: name 'name' is not defined

※ 辞書のキーは"(ダブルコーテーション)で囲わないとエラーになる。str.format()は囲わなくてもOKなのでハマりどころかもしれません。

{}内での計算

f-stringは{}内が先に計算された後に文字列展開される

val = 1.41421356
size = 4
f'res: {val:.{size}}'
# => res: 1.414

val = 2
f'Square {val*2}'
# => Square 4

{}内で関数も実行できる

def add(a, b):
  return a + b
x = 10
y = 20
f'value: {add(x, y)}'
# => value: 30

{ } を出力する

\ではなく、{{ }}の様に二重で囲むと{ }が出力できる

val = 100
f'{{}}-{val}-{{val}}-{{{val}}}-{{'
# => {}-100-{val}-{100}-{

※ 片方だけ{を出力したい場合も二重にしないとシンタックスエラーになる

書式の指定

展開する変数に:~で書式を指定して展開することが出来るようです。

num = 255
print( f'{num} to hex => {num:x}' )
# => 255 to hex => ff

d = {'year': 2018, 'month': 8, 'day': 15}
print( f'{d["year"]}-{d["month"]:02}-{d["day"]:02}' )
# => 2018-08-15

 
Pythonって書き方は1つのようなイメージがあったんのですが、文字列中の変数展開の方法も結構いろいろあるんだなと知る事ができました。
Python3からは基本的に、f-string (f'...')で良いかなという印象です。
展開する変数が配列や辞書の時は場合によってはstr.formatで位置引数(*)・キーワード引数(**)を使うほうが楽かもしれません。


[参考]

ゼロから作るDeep Learning ? ―自然言語処理編

ゼロから作るDeep Learning ? ―自然言語処理編