Pythonテクニック
以下のPythonプログラムはすごいナンセンスである。
流石にいないと思うが。。。
class Person(object):
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
a = Person("Taro")
print(a.get_name())
a.set_name("Jiro")
print(a.get_name())
person.set_name("Mr." + a.get_name())
print(a.get_name())
get_nameで名前を取得、set_nameで名前を設定している。
Taro
Jiro
Mr.Jiro
普通に書くと以下のように簡潔になる。
class Person(object):
def __init__(self, name):
self.name = name
a = Person("Taro")
print(a.name)
a.name = "Jiro"
print(a.name)
a.name += "Yamada"
print(a.name)
属性が設定されたときに特別な振る舞いをしたい場合、@property デコレータをつけて、@xxx.setterというふうにsetter属性をマイグレートする。
class NewPerson(Person):
def __init__(self, name):
super().__init__(name)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
self.current = "Mr." + self._name
a = NewPerson("Taro")
print(a.name)
print(a.current)
上記の場合、currentを呼び出すとMrがつく。
Taro
Mr.Taro
ちなみに、getterやsetterの中で、他の属性をsetしては絶対ダメ。
変な動作をする。
以下のようにするとバリデーションをかけることもできる。
class NewPerson(Person):
def __init__(self, name):
super().__init__(name)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if type(name) is not str:
raise ValueError("正しく名前を入力してください。")
self._name = name
a = NewPerson("Taro")
print(a.name)
a.name = 0
Taro
Traceback (most recent call last):
・・・
ValueError: 正しく名前を入力してください。
hasattrを使うと変更禁止にすることも可能。
@name.setter
def name(self, name):
if hasattr(self, 'name'):
raise AttributeError("名前は変更できません。")
self.__name = name
@property は使いすぎてはダメ。
属性とは
- プロパティ : データ (e.g. インスタンス変数)
- メソッド : 処理 (e.g. クラスメソッド)
dir()でオブジェクトが持ってる属性を調べられる。
user = NewPerson("Taro")
print(dir(user))
['_NewPerson__name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
Everything is object
Pythonのクラスは全てobjectというクラスから継承している。
object内の関数は全てDunderである。
Dunder(ダンダー)
__init__
のようにアンダースコア2つに囲まれている関数のことをDunder(Double underscore)と呼ぶ。
Special methods、Magic methodsともいわれる。
init(self)
インスタンスの初期化。インスタンスを生成すると実行される関数。object内で 2 番目に実行される。
new(cls):
インスタンスの生成。インスタンス創造時に実行される関数で、object内で 1 番目に実行される関数。
それ以外の特殊メソッドは公式ドキュメントに記載あり。
デコレータ
def deco(func):
def foo(*args, **kwargs):
print("Hello Deco!")
return foo
@deco
def a():
print('Hello Decorator!')
a() # Hello Deco!
デコレータを複数使う
デコレータが複数ある場合は下から順番に実行される。
以下の例ではdeco1 → deco2
def deco2(func):
def foo(*args, **kwargs):
res = func(*args, **kwargs) + " deco2!"
return res
return foo
def deco1(func):
def foo(*args, **kwargs):
res = func(*args, **kwargs) + " deco1"
return res
return foo
@deco2
@deco1
def main():
return 'Hello'
print(main()) # Hello deco1 deco2!
引数があってもOK
def deco1(func):
def foo(*args, **kwargs):
res = func(*args, **kwargs) + " deco1"
return res
return foo
@deco1
def main(str):
return f'Hello {str}'
print(main("world")) # Hello world deco1
デコレータの引数も使える
def deco(str):
def _deco(func):
def foo(*args, **kwargs):
res = func(*args, **kwargs) + " " + str
return res
return foo
return _deco
@deco('deco2!')
@deco('deco1')
def hoge():
return 'Hello'
print(hoge()) # Hello deco1 deco2!
デスクリプタ
デスクリプタとは__get__()
、 __set__()
、あるいは __delete__()
メソッドを定義したあらゆるオブジェクトのこと。set_name()も使うことができる。
データデスクリプタ
__set__()
と __delete__()
の両方を定義しているオブジェクト
非データデスクリプタ
__get__()
のみ定義されているオブジェクト
class B:
def __get__(self, obj, objtype):
print(self)
print(obj)
print(objtype)
return 'Hello, world!'
class A:
b = B() # デスクリプタインスタンス
a = A()
print(a.b)
<__main__.B object at 0xffffb8aeba50>
<__main__.A object at 0xffffb8aebb10>
<class '__main__.A'>
Hello, world!
デスクリプタ例2
objのパラメータにはAのインスタンス(a)が入る。
selfにはBのインスタンス(b)が、objectにはAのクラスが入る。
class B:
def __get__(self, obj, objtype=None):
print(obj.arg1)
print(obj.arg2)
return 'Hello, world!'
class A:
b = B()
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
a = A("foo", "hoge")
print(a.b)
foo
hoge
Hello, world!
とりあえず一部をqiitaに更新
【Python中・上級者向け】Dunderとは?