10.オブジェクト指向のPython

オブジェクト指向プログラミング-オブジェクト指向プログラミング、略してOOPは、プログラミングのアイデアです。 OOPはオブジェクトをプログラムの基本単位と見なし、オブジェクトにはデータとデータを操作するための関数が含まれています。

オブジェクト指向の初期認識####

プロセス指向と機能プログラミング#####

機械指向

機械の指示に抽象化されており、機械は理解しやすいです
表す:アセンブリ言語

プロセス指向

1つのことを行い、ステップを設定し、最初のステップで何をするか、2番目のステップで何をするか、状況Aがある場合は何をするか、Bがある場合は何をするか。
問題は規模が小さく、段階的に処理できます
代表者:C言語。

# プロセス指向のプログラミングは、オブジェクトの要素の数を測定します。
s1 ='fjdsklafsjda'
count =0for i in s1:
 count +=1

l1 =[1,2,3,4]
count =0for i in l1:
 count +=1

` プロセス指向のプログラミングと比較して、機能プログラミングには2つの最も明白な特徴があります。

# 1. コードの再利用性を減らす
# 2. コードの読みやすさを向上させる
機能プログラミングとオブジェクト指向プログラミング#####
# 機能プログラミング

# 認証関連
def login():
 pass

def regisgter():
 pass

# アカウントアカウント関連
def func1():
 pass

def func2():
 pass

# ショッピングカート関連
def shopping(username,money):
 pass
def check_paidgoods(username,money):
 pass
def check_unpaidgoods(username,money):
 pass
def save(username,money):
 pass
# オブジェクト指向プログラミング
classLoginHandler:
 def login(self):
  pass

 def regisgter(self):
  pass

classAccount:
 def func1(self):
  pass

 def func2(self):
  pass

classShoppingCar:
 def shopping(username,money):
  pass
 def check_paidgoods(username,money):
  pass
 def check_unpaidgoods(username,money):
  pass
 def save(username,money):
  pass

比較すると、オブジェクト指向の最初の利点がわかります

# オブジェクト指向プログラミング:同様の機能のコレクション,コードをより明確かつ合理的にします。

# 2番目の利点について説明する前に、オブジェクト指向とは何かを見てみましょう。

# オブジェクト指向プログラミングの中核はオブジェクト(神のような考え方)です。オブジェクトとは何かを理解するには、自分を神と見なす必要があります。神の目には、世界に存在するものはすべてオブジェクトであり、存在しないものを作成することができます。

# クラスとは何ですか?オブジェクトとは何ですか?

# クラス:同じプロパティと機能を持つもののクラス。

# オブジェクト:それはクラスの具体的な表現です。

# 具体的に:最初に⻋とは何かを説明します?ホイールがあります,方向プレート,エンジン付き,実行されるのは⻋.いいです.説明する.⼈とは.名前を付ける,年齢,趣味,歌ったり、踊ったり、考えたりできるのは一人です.つまり、広い意味では、人はタイプです。しかし、私の車では、あなたはオブジェクトです。

# 猫はあなたの家で育てられた一種の大きなみかんです。

# 犬は最初のタイプで、隣で育てられたエルハがターゲットです。

# 対面思考,自分でオブジェクトを作成するには.自分でシーンを作成する.あなたはオブジェクトの世界の神です.あなたはそれを手放したいですか.誰かにできるようにしたいですか?

2番目の利点について話しましょう:オブジェクト指向、問題を見るには神の視点が必要です、クラスは実際にはパブリックテンプレート(工場)であり、オブジェクトは特定のテンプレートからインスタンス化されます(ゆっくりと理解します)。

機能プログラミング

def func(s):
 count =0for i in s:
  count +=1return count
func('fdsafdsa')func([1,2,3,4])

オブジェクト指向プログラミング

コンピュータが解決しなければならない問題の規模が拡大するにつれて、状況はますます複雑になり、調整するために多くの人と多くの部門が必要になります。プロセス指向のプログラミングはあまりにも不適切です。
代表者:C ++、JAVA、Pythonなど。

オブジェクト指向####

オブジェクト指向とは何ですか?#####

世界を理解して分析し、すべてをカテゴリに抽象化するための方法論。

クラス

クラスは、抽象的な概念、すべての抽象化、および物事のクラスの共通の特性のコレクションです。
コンピュータ言語でクラスを記述することは、属性とメソッドのコレクションです。

オブジェクトインスタンス、オブジェクト

オブジェクトは、クラス、エンティティの具体的な表現です。
私たち一人一人にとって、この個人は抽象的な人間の異なる実体です。

オブジェクトとクラス

# クラス:オブジェクトのタイプ=>デジタル
# 同じ特性と動作を設定する

# オブジェクト:クラスの具体的なパフォーマンス=>ナンバー10
# クラスのインスタンス化は、実際に存在する特性と動作を持つ個人です(各オブジェクトは一意です))

# Example1
# あなたは魚を食べる
# あなたはオブジェクトです;魚も対象です;食べることは行動です
# あなたは特定の人であり、特定の対象です。あなたは人間に属しています、人間は抽象的な概念です、
# 無数の具体的な個人の抽象化です.そして、あなたが食べる特定の魚である特定のオブジェクトでもあります。
# この魚は魚に属しています、それは無数の魚によって抽象化された概念です.

# 食べる,是动作,也是操作,也是方法,这个食べる是你的动作,也就是人类具有的方法,
# 逆に言えば、魚は人を食べる、食べることは魚の行動です.
# 食べることは多くの動物が持っている行動です。人間と魚は動物のカテゴリーに属し、動物のカテゴリーは抽象的な概念です,
# 動物は皆食べますが、食べ方は異なります.
# あなたは車を運転します。この車は車のクラスの特定のオブジェクト(インスタンス)でもあります。このアクションを運転することは魚が所有するのではなく、人間が所有する方法です。.

# 属性,彼はオブジェクトの状態を抽象化したものであり、データ構造によって記述されます.
# 操作、彼はオブジェクトの動作を抽象化したものであり、操作の名前と操作を実装する方法で記述されます.

# 誰もが名前、身長、体重などの情報を持っています。これらの情報は個人の属性ですが、この情報を人間に保存することはできません。
# それは抽象的な概念であり、特定の値を保持できないためです.
# ヒューマンインスタンスは特定の人物であり、これらの特定の属性を保存することもでき、さまざまな人物がさまざまな属性を持つことができます。.

# 哲学
# すべてがオブジェクトです
# オブジェクトは、データと操作のカプセル化です
# オブジェクトは独立していますが、相互に作用することができます.
# 現在、OOPは人間の認識に最も近いプログラミングパラダイムです.
なぜオブジェクト指向のプログラミングが必要なのですか?#####

プロセス指向:開発コストが高く、問題解決の制限が小さい
オブジェクト指向:開発コストが低く、問題解決はオブジェクトに限定されます

質問: 'abc' => {'a'、 'b'、 'c'}
プロセス指向:手描き
オブジェクト指向:str =>リスト=>セット

開発:優先オブジェクト指向(問題を解決するためのオブジェクトを見つける)、
次に、複数のオブジェクト(オブジェクト指向とプロセス指向の組み合わせ)を見つけることを検討します。
最後に、問題を解決できるオブジェクトをカプセル化します(外部はオブジェクト指向の実施形態であり、内部の問題解決の中核はプロセス指向です)

クラスの関連知識####

クラスとオブジェクトの宣言#####
classClassName:
ステートメントブロック
# 1. classキーワードを使用する必要があります
# 2. クラス名はラクダケースを使用する必要があります
# 3. クラス定義が完了すると、ClassNameにバインドされたクラスオブジェクトが生成されます。.classStudent:"""YouMen"""
 age =21 #最初の部分:静的属性属性静的変数静的フィールド
 def foo(self):print(self.age)return self.age

print(Student)              #印刷
print(Student.__name__)     #クラス名を印刷する
print(Student.foo)          #印刷オブジェクトの機能
print(Student.age)          #印刷クラスの静的プロパティ
print(Student.__doc__)      #クラスでコメントを印刷する
print(type(Student))

# 客観化
mycls =Student()           #インスタンス化
print(type(Student))
mycls.foo()print(mycls.foo())<class'__main__.Student'>
Student
< function Student.foo at 0x000001C1E9FE1EA0>21
YouMen
< class'type'><class'type'>212121

# クラスオブジェクトとクラス属性
# クラスオブジェクト、クラスの定義はクラスオブジェクトを生成します
# クラス属性, 类定义中的变量和类中定义的方法都是クラス属性.
# クラス変数,xはStudentの属性です,__doc__クラスの属性でもあります
# 食べることが人間の方法である場合、fooメソッドはクラスの属性ですが、すべての特定の人が食べることができます,
# 言い換えれば、人間のインスタンスによってのみ呼び出すことができるメソッド.
# fooはメソッドオブジェクトであり、通常の関数オブジェクト関数ではありません。少なくとも1つのパラメーターが必要です。
# そして最初のパラメータは自己でなければなりません(自己は名前を変えることができます),このパラメータの位置は自分に任されています。
# ここで、selfは現在のインスタンス自体を指します.
オブジェクトとは何ですか?

クラス名が()を追加する限り、オブジェクトはクラスから出てきます。これはインスタンス化プロセスであり、これによりオブジェクトがインスタンス化されます。
オブジェクトをインスタンス化するためにdemo1 = ClassName()を作成すると、彼は自動的に__init__メソッドを実行します。

オブジェクトのインスタンス化はどうなりましたか?

# 1. メモリ内のオブジェクトスペースを開きます.
# 2. クラスで自動的に実行__init__方法,そしてこのオブジェクトスペース(メモリアドレス)に渡されます__init__メソッドの最初の位置パラメータself.
# 3. に__init__メソッドのselfを介してオブジェクトスペースに属性を追加します
オブジェクト操作オブジェクトスペース属性#####
# __ init__方法
# MyClass()実際に呼んでいます__init(self)メソッドは定義できません。定義されていない場合は、インスタンス化後に暗黙的に呼び出されます.
# 効果:インスタンスを初期化します.classStudent:"""this is a class"""
 mind ='思いやりのある'
 language ='使用言語'
 def __init__(self,name,sex,age,hobby):
  # selfとobjが同じメモリアドレスと同じスペースを指している場合、以下は、selfを介してこのオブジェクトスペースの4つの属性をカプセル化することです。
  self.n = name
  self.s = sex
  self.a = age
  self.h = hobby

obj =Student('youmen','男性性','18','移動')print(obj.__dict__){'n':'youmen','s':'男性性','a':'18','h':'移動'}

オブジェクト操作オブジェクトの単一の属性であるユニバーサルポイント

obj =Student('youmen','男性','18','移動')
obj.job ='python'      #属性ジョブの追加
del obj.n               #Objのname属性を削除します
obj.s ='女性'            #変化する
print(obj.s)print(obj.__dict__)     #小切手

# 女性
# {' s':'女性','a':'18','h':'移動','job':'python'}

クラスのオブジェクトビュープロパティ

obj =Student('youmen','男性','18','移動')print(obj.mind)print(obj.language)
obj.a =88print(obj.a)

# 思いやりのある
# 使用言語
# 88

オブジェクト操作クラスのメソッド

classStudent:"""this is a class"""
 mind ='思いやりのある'
 language ='使用言語'
 def __init__(self,name,sex,age,hobby):
  # selfとobjが同じメモリアドレスと同じスペースを指している場合、以下は、selfを介してこのオブジェクトスペースの4つの属性をカプセル化することです。
  self.n = name
  self.s = sex
  self.a = age
  self.h = hobby

 def work(self):print(self)print('人間は働きます')

 def tools(self):print('人間はツールを使用します')
obj =Student('youmen','男性','18','深い思考')
obj.work()
obj.tools()

# <__ main__.Student object at 0x0000021E46230588>
# 人間は働きます
# 人間はツールを使用します

クラス内のメソッドは通常、オブジェクトによって実行され(クラスメソッドと静的メソッドを除く)、これらのメソッドを実行するオブジェクトは、オブジェクトスペースをメソッドの最初のパラメーターselfに自動的に渡します。

自己とは何ですか?
# selfは実際にはクラスのメソッドです(関数)最初の位置パラメータ,只不过解释器会自动调用这个関数的对象传给self,所以咱们把类中的方法第一个参数约定俗称设置为self,これがオブジェクトであることを表します.

# クラスは複数のオブジェクトにインスタンス化できます

クラスの空間問題とクラス間の関係####

クラススペースの問題#####

` オブジェクトプロパティと静的プロパティはどこに追加できますか?

classA:
 def __init__(self,name):
  self.name = name

 def func(self,sex):
  self.sex = sex

 def func1(self):
  A.bbb ='ccc'

# クラス外にオブジェクトプロパティを追加する
obj =A('youmen')
obj.age =18print(obj.__dict__)

# クラス外に静的プロパティを追加する
A.aaa ='zhou'print(A.__dict__)

# クラスにオブジェクトプロパティを追加します
obj =A('flying')   # __inif__メソッドはできます
obj.func('男性')print(obj.__dict__)

# クラスの内部も追加できます
A.func1(123)print(A.__dict__)

# {' name':'youmen','age':18}
# {'__ module__':'__main__','__init__':<function A.__init__ at 0x0000019395441EA0>,'func':<function A.func at 0x0000019395441D90>,'func1':<function A.func1 at 0x0000019395441F28>,'__dict__':<attribute '__dict__'of'A' objects>,'__weakref__':<attribute '__weakref__'of'A' objects>,'__doc__': None,'aaa':'zhou'}

# {' name':'flying','sex':'男性'}
# {'__ module__':'__main__','__init__':<function A.__init__ at 0x0000019395441EA0>,'func':<function A.func at 0x0000019395441D90>,'func1':<function A.func1 at 0x0000019395441F28>,'__dict__':<attribute '__dict__'of'A' objects>,'__weakref__':<attribute '__weakref__'of'A' objects>,'__doc__': None,'aaa':'zhou','bbb':'ccc'}

オブジェクトがクラスの属性を見つける方法

オブジェクトスペースにクラスオブジェクトポインタがあるため、オブジェクトはクラスを見つけることができます。

# オブジェクトルックアップシーケンス:最初にオブジェクトスペースから見つけます--->クラススペース検索--->親スペース検索
# 属性のクラス名検索順序:このタイプのスペースを最初に探します--->親スペース検索

クラスとクラスの関係

# 1. 依存
# 2. 接続関係
# 3. 組み合わせ関係
# 4. 集約関係
# 5. 実現関係
# 6. 継承

オブジェクト指向の継承####

継承とは何ですか?

継承(英語:継承)は、オブジェクト指向のソフトウェアテクノロジの概念です。カテゴリAが別のカテゴリBから「継承」する場合は、これを「Bのサブカテゴリ」と呼び、Bを「Aの親カテゴリ」または「BはAのスーパーカテゴリ」と呼びます。継承により、同じコードを再度記述する必要なしに、サブカテゴリに親カテゴリのさまざまなプロパティとメソッドを持たせることができます。サブカテゴリに親カテゴリを継承させながら、特定のプロパティを再定義し、いくつかのメソッドを書き直すことができます。つまり、親カテゴリの元のプロパティとメソッドを上書きして、親カテゴリからさまざまな機能を取得できます。さらに、サブカテゴリに新しい属性とメソッドを追加することも一般的な方法です。一般的な静的オブジェクト指向プログラミング言語では、継承は静的です。つまり、サブカテゴリの動作はコンパイル時に決定され、実行中に拡張することはできません。
文字通りの意味は次のとおりです。息子は父親の継承を継承し、家族の財産を合法的に継承します。つまり、あなたが唯一の子供であり、あなたも非常に親密である場合、あなたはすべての両親の財産を継承し、すべての財産はあなたによって使用されます(放蕩息子を除く)

classPerson:
 def __init__(self,name,sex,age):
  self.name = name
  self.age = age
  self.sex = sex

classCat:
 def __init__(self,name,sex,age):
  self.name = name
  self.age = age
  self.sex = sex

classDog:
 def __init__(self,name,sex,age):
  self.name = name
  self.age = age
  self.sex = sex

# 継承された使用法:
classAniaml(object):
 def __init__(self,name,sex,age):
   self.name = name
   self.age = age
   self.sex = sex

classPerson(Aniaml):
 pass

classCat(Aniaml):
 pass

classDog(Aniaml):
 pass

継承された利点:

1. クラスのカップリングを増やします(カップリングは多すぎないようにし、問題ないようにします)

2. 重複コードの削減

3. コードをより標準化および合理化する

継承カテゴリ#####
# 継承は、単一の継承と複数の継承に分けることができます

# Python2xバージョンには2つのクラスがあります
# クラシックと呼ばれるもの,Python2の場合.2より前は、クラシッククラスが使用されていました。クラシッククラスが基本クラスのルートに記述されていない場合.
# 新しいクラス,Python2の場合.2の後、基本クラスのルートがObjectクラスであることを特徴とする新しいクラスが出現しました。.

# Python3xバージョンにはクラスが1つだけあります
# Python3は新しいスタイルのクラスを使用します。基本クラスを継承する人がいない場合、このクラスはObjectを継承します。
単一の継承#####

クラス名、オブジェクトは親クラスメソッドを実行します

classAniaml(object):
 type_name ='動物'

 def __init__(self,name,sex,age):
   self.name = name
   self.age = age
   self.sex = sex

 def eat(self):print(self)print('何か食べる')classPerson(Aniaml):
 pass

classCat(Aniaml):
 pass

classDog(Aniaml):
 pass

# クラス名:
print(Person.type_name)  #親クラスのプロパティとメソッドを呼び出すことができます。
Person.eat(111)print(Person.type_name)

# オブジェクト:
# オブジェクトをインスタンス化します
p1 =Person('春の兄弟','男性',18)print(p1.__dict__)
# # オブジェクト実行クラスの親クラスのプロパティとメソッド。
print(p1.type_name)
p1.type_name ='666'print(p1)
p1.eat()

# クラス名、オブジェクトはそれぞれ親クラスメソッドを呼び出します

実行順序

classAniaml(object):
 type_name ='動物'
 def __init__(self,name,sex,age):
   self.name = name
   self.age = age
   self.sex = sex

 def eat(self):print(self)print('何か食べる')classPerson(Aniaml):
    
 def eat(self):print('%食べる'%self.name)classCat(Aniaml):
 pass

classDog(Aniaml):
 pass

p1 =Person('barry','男性',18)
# オブジェクトをインスタンス化するときに実行する必要があります__init__方法,クラスではなく、親クラスから検索し、親クラスではなく、オブジェクトクラスから検索します。
p1.eat()
# 最初に独自のクラスでeatメソッドを実行する必要があり、親クラスのメソッドは独自のクラスなしでのみ実行できます。

# 実行順序

クラスメソッドと親クラスメソッドの同時実行

**方法1 **

親クラスのfuncメソッドを実行する場合、このメソッドはサブクラスでも使用され、サブクラスのメソッドに書き込みます。

親class.func(object、other parameters)

classAniaml(object):
 type_name ='動物'
 def __init__(self,name,sex,age):
   self.name = name
   self.age = age
   self.sex = sex

 def eat(self):print('何か食べる')classPerson(Aniaml):
 def __init__(self,name,sex,age,mind):'''
  self = p1
  name ='春の兄弟'
  sex ='laddboy'
  age =18
  mind ='思いやりのある''''
  # Aniaml.__init__(self,name,sex,age)  #方法1
  self.mind = mind

 def eat(self):super().eat()print('%食べる'%self.name)classCat(Aniaml):
 pass

classDog(Aniaml):
 pass

# 方法1:Aniaml.__init__(self,name,sex,age)
# p1 =Person('春の兄弟','laddboy',18,'思いやりのある')
# print(p1.__dict__)

# 方法1がわからない場合:
# def func(self):
#  print(self)
# self =3
# func(self)
複数の継承#####
classShenXian: #不滅
 def fei(self):print("妖精の街⻜")classMonkey: #モンキー
 def chitao(self):print("サルは桃を食べるのが好きです")classSunWukong(ShenXian, Monkey): #モンキーキングは神です,同時に猿
 pass
sxz =SunWukong() #モンキーキング
sxz.chitao() #桃が食べられる
sxz.fei() #ミーティング⻜

現時点では、モンキーキングはサルであり、神でもあります。そのモンキーキングはこの2つのクラスを継承し、モンキーキングはこれら2つのクラスのメソッドを自然に実行できます。複数の継承は使いやすく、非常に優れています。わかりますが、複数の継承では、このような問題があります。2つのタイプのメソッドが同じ名前の場合、どうすればよいですか?次に、メソッドのタイプを見つける方法が含まれます。これは問題です。つまり、MRO(メソッド解決順序)の問題です。これは、Pythonでは非常に複雑な問題です。Pythonのバージョンが異なれば、MROを完了するために異なるアルゴリズムを使用するためです。

` python3にはクラシッククラスはありませんが、クラシッククラスのMROを学習でき、筆記テストがある場合があります。

# Example3
# マルチパラメータ
classPerson:
 x ='flying'
 def __init__(self,name,age=19):
  self.name = name
  self.y = age
 """ this is a Person class"""

 def show(self,x,y):print(self.name,self.x,x,y)
  self.y = x
        
a =Person('tom')
b =Person('alice',18)print(a.name,a.y,b.name,b.y)print(a.x,b.x)
a.show(a.y,b.y)print(a.y)
# __ init__()メソッドは戻り値を持つことができず、Noveのみを返します
# クラスがインスタンス化された後、インスタンスオブジェクトであるオブジェクトが取得されます.
# 上記の例のトム,アリスは人のインスタンスです.
# __ init__メソッドの最初のパラメータselfはインスタンスを参照します.
# Example4
classStudent:
 def __init__(self):print('self in init = {}'.format(id(self)))

c =Student()   #電話します__init__
print('c = {}'.format(id(c)))

# 出力は次のとおりです。
self in init =46710672
c =46710672

# 上記の例は、自己が発信者であることを示しています,cに対応するインスタンスオブジェクトですか.
# 自己という名前は単なる慣習であり、彼はそれを変更することができます,ただし、変更しないでください。変更すると、コードの読みやすさに影響します。.
# Example6
classPeople:
 name ='人'

p1 =People()
p2 =People()

# 結論1:クラスと各オブジェクトの名前は独立しています
print(p1.__dict__)print(p2.__dict__)print(People.__dict__)

# 結論2:クラスとすべてのオブジェクトは、クラス内の名前を使用できます
print(People.name)print(p1.name)print(p2.name)

# 結論3:オブジェクトアクセス名、それ自体への優先アクセス、クラス自体へのアクセスはこれ以上ありません
p1.name ='張さん'
p2.user ='Li Si'print(People.name)print(p1.name)print(p2.user)print(p2.name)

# フォーカス
# オブジェクト操作名、操作はオブジェクト、クラス操作名はクラスであり、相互に干渉しません
# クラスはクラスの名前にのみアクセスできます
# オブジェクトアクセス名、それ自体への優先アクセス、ただしクラスへのアクセスはこれ以上ありません

{}{}{'__ module__':'__main__','name':'人','__dict__':'__weakref__'of'People' objects>,'__doc__': None}
人
人
人
人
張さん
Li Si
人

クラス初期化メソッド

# クラスの各オブジェクトをすばやくインスタンス化して、オブジェクトの名前名に複数の名前を付けることができます

classNewTeacher:
 def __init__(self,name,sex,age):
  # print(id(self))    #自己はインスタンス化によって生成されたオブジェクトです(nt1)
  # print('initは呼ばれます')
  self.name = name
  self.sex = sex
  self.age = age
 pass

# Class()はクラスを呼び出しています__init__方法
nt1 =NewTeacher('YouMen','Man',30)print(nt1.name,nt1.sex,nt1.age)

nt2 =NewTeacher('flying','Man',40)print(nt2.name,nt2.sex,nt1.age)

クラスメソッド分類

オブジェクトメソッド:直接定義されたメソッド。オブジェクトから呼び出すことをお勧めします。クラス内のメソッドは、オブジェクトのデータを使用してオブジェクトメソッドを定義する必要があります。

  1. オブジェクトメソッドオブジェクト呼び出し、デフォルトオブジェクトが最初のパラメータに渡されます
クラスクラス名
	def fn(self,*args,**kwargs):pass

クラスメソッド:classmethodで変更されたメソッドは、クラスで呼び出すことをお勧めします。クラスのデータをクラス内で使用する必要がある場合のメソッドは、クラスメソッドとして定義する必要があります。

  1. classメソッドはクラスによって呼び出され、クラスはデフォルトで最初のパラメーターに渡されます。
クラスクラス名:
 @ classmethod
 def fn(cls,*args,**kwargs): pass
  1. 静的メソッドはクラスから呼び出すことをお勧めします。デフォルトでは、呼び出し元は渡されません。
@ staticmethod
def fn(*args,**kwargs): pass

Example1:

classBook:
 name ='本'
 def __init__(self,name,price):
  self.name = name
  self.price = price
 # 書籍の詳細=>その本を知っている必要があります
 # @ classmethodクラス呼び出しclsはクラスであり、オブジェクト呼び出しはオブジェクトに処理されます.__class__
 def detail(self):
  # print(cls.name)print('%sの価格は:%s元'%(self.name,self.price))

book1 =Book('西への旅',29.8)
book2 =Book('水マージン',98.9)
book1.detail()
book2.detail()
# print(book1.__class__)

# 静的メソッド: 方法的内部不需要对象及类的参与,所以定义为静的メソッド,ただし、メソッドは呼び出し元が呼び出す必要があります。クラスを使用することをお勧めします。.classNumTool:  #ツール=>モジュール
 def max_two(self,n1,n2):
  max_num = n1 if n1 > n2 else n2
  print('大きな数字は%s'% max_num)
 @ staticmethod
 def new_max_two(n1,n2):
  max_num = n1 if n1 > n2 else n2
  print('大きな数字は%s'% max_num)

n1 =NumTool()
n2 =NumTool()
n1.max_two(10,20)
n2.max_two(10,20)

NumTool.new_max_two(10,20)
n1.new_max_two(10,20)

Example2:

# クラスメソッド: 方法的内部需要类的参与,所以定义为クラスメソッド,第一个参数默认传参
classNewNumTool:
 PI =3.14
 @ classmethod
 def new_max_two(cls,n1,n2):
  max_num = n1 if n1 > n2 else n2
  return max_num
    
 @ classmethod
 def new_max_three(cls, n1, n2, n3):
  # max_num ="新しいものを再利用したい_max_two"
  max_num = cls.new_max_two(n1, n2)
  max_num = cls.new_max_two(max_num, n3)return max_num

 @ classmethod
 def is_PI(cls,num):if num == cls.PI:return True
  return False

reslt = NewNumTool.new_max_three(1,5,3)print('大きな数字は%s'% reslt)print(NewNumTool.is_PI(3.14))

大きな数字は5です
True

3つのオブジェクト指向機能####

パッケージ #####

カプセル化とは何ですか?

# 「パック」とは、一連の属性をコンテナに入れることを意味します
# " 密閉する"それを隠すための手段、それは内側に見えるが、それは外側から隠されている.

# パッケージ: 把很多数据パッケージ到⼀个对象中. 把固定功能的代码パッケージ到⼀个代码块,関数,オブジェクト,モジュールにパッケージ化. 这都属于パッケージ的思想.特定の状況の特定の分析.といった.あなたは非常に大きな関数を書きました. 那这个也可以被称为パッケージ.向かい合う物体の心の中で.一見取るに足らないコンテンツを1つのストレージにまとめて使用することです. 这就是パッケージ.

なぜカプセル化を使用するのですか?

# 複雑な操作を単純なインターフェイスにカプセル化し、インターフェイスの呼び出しプロセスを厳密に制御します

カプセル化の使用方法

# ただし、二重下線で始まる属性(二重下線で終わることはできません)は非表示になり、クラス内で直接使用できます。
# クラスの外部を直接使用することはできません。つまり、外部の内部にないパッケージを使用することはできません。

# この隠された機能:
# 1. これは単なる文法的な変形であり、最初の属性を次のように変換します。:独自のクラス名属性名(n=1 # __Foon=1)
# 2. この変換は、クラス定義フェーズで1回だけ発生し、クラス定義フェーズの後に新しい変換が発生します。__最初の属性は変形されません.
# 3. 隠蔽は内部ではありません
# 4. 継承では、親クラスがサブクラスに同じ名前の独自のメソッドをカバーさせたくない場合は、メソッドをプライベートにすることができます.

Example1

classTeacher:
 def __init__(self,name,age):
  # self.__name=name
  # self.__age=age
  self.set_info(name,age)

 def tell_info(self):print('名前:%s,年齢:%s'%(self.__name,self.__age))
 def set_info(self,name,age):if not isinstance(name,str):
   raise TypeError('名前は文字列タイプである必要があります')if not isinstance(age,int):
   raise TypeError('年齢は整数でなければなりません')
  self.__name=name
  self.__age=age

t=Teacher('tom',18)
t.tell_info()

t.set_info('flying',19)
t.tell_info()

Example2

classStudent(object):
 def __init__(self,name,score):
  self.name=name
  self.score=score
 def get_grade(self):if self.score >=90:return'A'
  elif self.score>=60:return'B'else:return'C'

Tom =Student('Tom',99)
Alice =Student('Alice',57)print(Tom.name,Tom.get_grade())print(Alice.name,Alice.get_grade())

外部梱包方法

# カプセル化とは:クラスの次の属性とメソッドは、外部から隠されて内部に表示され、プライベート変数になります。
# なぜパッケージするのか:属性とメソッドの操作に対するアクセス許可を追加します。特定のアクセス許可はカスタムロジックを介して処理されます

# カプセル化の手段:クラス属性メソッド、オブジェクト属性メソッド、および静的メソッド名の前に追加します__
# それが通過する限り__名前、この命名規則は、隠すことです
# 自然: __ファーストネーム 封装隐藏白能量的自然是 将ファーストネーム修饰成_クラス名__ファーストネーム

# パッケージを外部で解決する方法
# 1. 外の世界にアクセスしたくない場合は、データにアクセスする方法を提供しません。.
# 2. 外の世界にアクセスしたい場合は、データにアクセスする方法を提供できます。メソッドにはロジックがあり、操作権限を追加できます。.
classTest:
 def __init__(self,name):
  # __ 名前は外側からのみ隠され、内側に表示されます
  self.__name = name

 def get_name(self):return self.__name

 def set_name(self,name):if'sh' not in name:    #データを変更するとデータセキュリティの問題が発生する可能性があり、制限を追加できます
   self.__name = name

# フォーカス:カプセル化された外部アクセス構文の最適化
classUser:
 def __init__(self,name):
  self.__name = name

 @ property #メソッドを属性として偽装する
 def name(self):return self.__name

 @ name.setter #偽装されたgetメソッドを持つ(method)属性のsetメソッドを偽装できます
 def name(self, value):
  self.__name = value
    
 @ name.deleter
 def name(self):
  del self.__name

# 総括する
# 1. オブジェクトがなくなった、オブジェクトのプロパティがなくなったので、プロパティは必要ありません@ファーストネーム.deleter
# 2. getメソッドを外部に提供することが基盤です. @property,そうでなければ、外の世界は読み書きできません
# 3. もしあれば@property,あなたはできる@ファーストネーム.setter,設定しました,読み取りと書き込み、設定なしの読み取り専用.

@ property #偽装属性メソッド、必ずしも持っている必要はありません__ファーストネームアドバンスは対応します
def pwd(self):return'123456'

u1 =User('Ower')print(u1.name)
# del u1.name
print(u1.name)

Owen
継承#####
# 1. 継承とは何ですか?
# 継承は、新しいクラスを作成する方法です。新しいクラスはサブクラスまたは派生クラスと呼ばれ、継承されたクラスは親クラスまたは基本クラスまたはスーパークラスと呼ばれます。
# サブクラスは親クラスのすべてのプロパティとメソッドを継承し、これらのプロパティとメソッドを直接使用できます
# サブクラスは、プライベート属性を除くクラス内のすべてのコンテンツを自動的に所有できます.話しました,お父さんのものを自由に使うことができます.でも友達,私は一つのことをはっきりと認識しなければなりません.最初にお父さんがいる必要があります,後.秩序を乱すことはできません,pythonでの継承の実装は非常に簡単です.クラスを宣言するとき,クラス名の後に小さな括弧を追加します,継承関係.では、どのような状況で継承を使用できますか??純粋にコードレベルから.2つのクラスが同じ機能または機能を持っている場合.継承の形を取ることができます.クラスを抽出する,このクラスでは、2つのクラスの同じ部分が書かれています.次に、2つのクラスはこのクラスを別々に継承できます。.このように書くことの利点は、多くの反復的な関数やコードを書くことを避けることができるということです.セマンティクスから分析すると.はるかに簡単になります.xがコンテキストに表示される場合、それは一種のyです.現時点では,yは一般化された概念です.xはyよりも具体的です.その場合、xはyのサブクラスです.といった.猫は動物です.猫は動物を受け継ぐ.動物の可動性.猫は動くことができます.現時点では猫在创建的时候就有了移動する物的"移動する"この属性.もう一つの例,ホワイトボーンスピリットはモンスターです.YoukaiTianには比較的悪い機能があります"食べる",ホワイトボーンスピリットは出てくる方法を知っています"食べる".この時、白い骨の精霊は妖精を継承します.

# 2. 継承を使用する理由
# コードの冗長性を減らす
# サブクラスに親クラスのすべての関数を取得させます.

次の4匹の動物を実装したいとします

# Dog -犬
# Bat -コウモリ
# Parrot -オウム
# Ostrich -オーストリッチ

哺乳類と鳥に応じて分類すると、次のようなクラスの階層を設計できます。

「走れる」と「飛べる」で分類すると、そのようなクラスレベルを設計する必要があります

上記の2つのカテゴリを含める場合は、より多くのレベルを設計する必要があります

* 哺乳類:  能跑的哺乳類,能飞的哺乳類
* 鳥:  能跑的鳥,能飞的鳥.
# このように、クラスレベルは複雑です.

「ペットクラス」と「非ペットクラス」を追加したい場合、それを続けると、クラスの数が指数関数的に増加します。明らかに、この設計は機能しません。正しいアプローチは、複数の継承です。まず、メインクラスの階層まだ哺乳類と鳥に合わせて設計されています

classAnimal(object):
 pass

# 大きなカテゴリー:classMammal(Animal):
 pass

classBird(Animal):
 pass

# 様々な動物:classDog(Mammal):
 pass

classBat(Mammal):
 pass

classParrot(Bird):
 pass

classOstrich(Bird):
 pass

ここで、RunnableとFlyableの機能を動物に追加します。最初に、RunnableクラスとFlyableクラスを定義するだけで済みます

classRunnable(object):
 def run(self):print('Running...')classFlyable(object):
 def fly(self):print('Flying...')

Runable機能が必要な動物の場合、DogなどのRunnableがもう1つ継承されます。

classDog(Mammal, Runnable):
 pass

**Flyable機能を必要とする動物の場合、Bat:**などのFlyableがもう1つ継承されます。

classBat(Mammal, Flyable):
 pass
# 複数の継承により、サブクラスは複数の親クラスのすべての機能を同時に取得できます.
# 3. 使い方
**Example1**classParent1:
 pass
classParent2:
 pass
classSon1(Parent1):
 pass
# Pythonは複数の継承をサポートし、クラスは複数の親クラスを持つことができますが、javaには1つしかありません
classSon2(Parent2):
 pass
print(Son1.__bases__)print(Son2.__bases__)(<class'__main__.Parent1'>,)(<class'__main__.Parent2'>,)

# pythonインタープリター3を切り替えます.x >>>2.xは結論を導きます
"""
python3では、クラスの明示的な継承がない場合、オブジェクトクラスはデフォルトで継承されます
python2では、クラスが明示的に継承されていない場合、オブジェクトクラスは継承されません

pythonには2つのタイプのクラスがあります。
  新しいスタイル:
   オブジェクトまたはそのサブクラスを継承するクラスはすべて新しいスタイルのクラスです
   >>>: python3のすべてのクラスは新しいスタイルのクラスです
  クラシック
   オブジェクトクラスを継承せず、このクラスのサブクラスはすべてクラシッククラスです
   これは、クラシッククラスと新しいスタイルクラスの概念がpython2でのみ利用可能であることを意味します
   python3には新しいスタイルのクラスしかありません
"""

継承+派生/派生に基づいてコードの冗長性を減らす場合

オブジェクト間の類似した特性とスキルを要約する
クラス間で類似した属性と特性を要約して、親クラスを取得します

Example2

import pickle

classOldboyStudent:
 school ='oldboy'

 def __init__(self,name,age,sex):
  self.name = name
  self.age = age
  self.sex = sex

 def choice_course(sel):print('%s is choosing course'%self.name)

 def save(self):withopen(self.name,'wb')as f:
   pickle.dump(self,f)classOldboyTeacher:
 school ='oldboy'

 def __init__(self,name,age,sex,level):
  self.name = name
  self.age = age
  self.sex = sex
  self.level = level

 def score(self):print('%s is score'%self.name)

 def save(self):withopen(self.name,'wb')as f:
   pickle.dump(self,f)
stu =OldboyStudent('alice',22,'male')
stu.save()

tea =OldboyTeacher('Tom',32,'male',10)
tea.save()

# 振り返って、上記のコードに同様の部分があるかどうかを確認しましょう。クラス間のコードの冗長性を解決する方法を学びました。
classOldboyPeople:
 school ='oldboy'

 def save(self):withopen(self.name,'wb')as f:
   pickle.dump(self, f)
# 継承されたクラスの予備抽出。継承後にオブジェクトがプロパティとメソッドを見つける順序を考慮します。
stu.save()
stu.school

# 属性とメソッドが親クラスに抽出されることを説明しましたが、initには重複するコードもあり、これも抽出する必要があります
classOldboyPeople:
 school ='oldboy'
 def __init__(self,name,age,sex):
  self.name = name
  self.age = age
  self.sex = sex

 def save(self):withopen(self.name,'wb')as f:
   pickle.dump(self, f)
# 教師と学生のinitの違いに関係なく、最初に親クラスの統一されたinitを継承し、親クラスのinitも使用できることを確認します。

# 派生コンセプト
# サブクラスは親クラスのすべてのプロパティとメソッドを使用できますが、いくつかの追加のプロパティとメソッドもあります。
# 少し考えて:派生したプロパティとメソッドがたまたま親クラスのものと同じである場合、プロパティとメソッドを探すときに最初に誰を見つける必要がありますか?>>>まだ検索順

# 振り返ってみると、教師のinitには追加のレベルパラメータがあり、デフォルトのパラメータを親クラスに追加しないでください。
# initメソッドは自分で書き直すことしかできませんが、コードが重複しています
 def __init__(self,name,age,sex,level):
  OldboyPeople.__init__(self,name,age,sex)
  self.level = level
"""
サブクラスから派生した新しいメソッドで親クラスのメソッドを再利用します
方法1:名前と姓による訪問は継承とは何の関係もありません
OldboyPeople.__init__(self,name,age,sex)
その意味するところは、継承に関連する親クラスのメソッドを再利用する方法もあるということです。心配しないでください。

継承の原則

classFoo:
 def f1(self):print('Foo.f1')
    
 def f2(self):print('Foo.f2')
  self.f1() # obj.f1()classBar(Foo):
 def f1(self):print('Bar.f1')

obj=Bar()
obj.f2()

Foo.f2
Bar.f1

複数の継承

# F(A,B,C)新しいスタイルのクラスであろうとクラシックなクラスであろうと、すべて左から右に1つずつ下に移動して、デモ用のpythonバージョンを描画および切り替えます。
# ファイルヘッダーコーディングを追加することを忘れないでください:utf-8
# 2、 マルチ継承属性ルックアップ &quot;:オブジェクト自体-》オブジェクトクラス-》左から右に1本の枝を探す

classD:
 # def test(self):
 # print('D')
 pass

classE:
 def test(self):print('E')classF:
 def test(self):print('F')classA(D):
 # def test(self):
 # print('A')
 pass

classB(E):
 def test(self):print('B')classC(F):
 def test(self):print('C')classG(A,B,C):
 # def test(self):
 # print('G')
 pass

obj=G()
obj.test()

B

戻って、継承を通じて親クラスを呼び出すメソッドを見てください

# 方法2:スーパー(自分のクラス名,self).親クラスのメソッド名()
# superを呼び出すと、親クラスのメソッドを参照するために特に使用される特別なオブジェクトが取得されます。
# 具体的には、オブジェクトは、現在のクラスのMROリストに厳密に従って、現在のクラスの親クラスから属性を検索します。つまり、このメソッドは、継承に厳密に依存しています。
# ps:python3ではsuperと省略できます。()classOldboyPeople:
 def __init__(self,name,age,sex):
  self.name = name
  self.age = age
  self.sex = sex

classOldboyTeacher(OldboyPeople):
 def __init__(self,name,age,sex,level):
  # OLdboyPeople.__init__(self,name,age,sex)super(OldboyTeacher,self).__init__(name,age,sex)
  self.level=level
tea1=OldboyTeacher('alice',18,'male',10)print(tea1.name,tea1.level)classA:
 def f1(self):print('A')super().f2()    # super()現在および検索位置に基づいて検索を続行します

 def f2(self):print('A')classB:
 def f2(self):print('B')classC(A,B):
 def f2(self):print('C')
obj=C()print(C.mro())
obj.f1()

alice 10[<class'__main__.C'>,<class'__main__.A'>,<class'__main__.B'>,<class'object'>]
A
B
# 実際の作業では、複数の継承を慎重に使用してください。プログラミングはデカップリングですが、継承は強力なカップリングです。.

Mixin

クラスの継承関係を設計する場合、通常、メインラインは単一の継承から継承されます。たとえば、OstrichはBirdから継承しますが、追加の機能を混在させる必要がある場合は、複数の継承によって実現できます。たとえば、OstrichにBirdから継承させます。 、そして同時にRunnableを継承し、このデザインはMixinと呼ばれます。

継承関係をわかりやすくするために、 Runnable FlyableRunnableMixIn FlyableMixInに変更しました。同様に、肉食動物の「CarnivorousMixIn」と草食動物の「HerbivoresMixIn」を定義して、動物が同時に複数のMixInを持つようにすることもできます。

classDog(Mammal, RunnableMixIn, CarnivorousMixIn):
 pass

MixInの目的は、クラスに複数の関数を追加することです。このように、クラスを設計するときは、複数のレベルの複雑な継承関係を設計するのではなく、複数の継承を通じて複数のMixInの関数を組み合わせることが優先されます。
Pythonに付属する多くのライブラリもMixInを使用しています。たとえば、Pythonには TCPServer UDPServerの2種類のネットワークサービスが付属しています。複数のユーザーに同時にサービスを提供するには、マルチプロセスモデルまたはマルチスレッドモデルを使用する必要があります。これら2つのモデルは、 ForkingMixIn ThreadingMixInで構成されます。提供します。組み合わせることで、適切なサービスを作成できます。

たとえば、マルチプロセスモードでTCPサービスを作成する場合、定義は次のとおりです。

classMyTCPServer(TCPServer, ForkingMixIn):
 pass

次のように定義されたマルチスレッドUDPサービスを記述します。

classMyUDPServer(UDPServer, ThreadingMixIn):
 pass

**より高度なcoroutineモデルを開発する場合は、 CoroutineMixIn:**を作成できます。

classMyTCPServer(TCPServer, CoroutineMixIn):
 pass

**このように、異なるクラスの機能を組み合わせることを選択する限り、複雑で巨大な継承チェーンは必要ありません。必要なクラスをすばやく構築できます。**概要

Pythonでは複数の継承が許可されているため、MixInは一般的な設計です。
単一の継承のみを許可する言語(Javaなど)は、MixInの設計を使用できません。

多形#####

多形性とは

# 同じものの複数の形態(動物:人、猫、犬)
# 同じオブジェクト,複数のフォーム.これは実際にはpythonで説明するのは簡単ではありません.いつも使っているから.具体的には言わなかった.といった.変数を作成します=10,現時点では、aは整数型であることがわかっています。.しかし、私たちはプログラムすることができます="alex",現時点では,a⼜は文字列型になります.これは私たち全員が知っていることです.だが,私があなたに伝えたいのは.これは多形です.同じ変数aは複数の形式を持つことができます。

多形性を使用する理由

# 多形性:オブジェクトの特定のタイプを考慮せずにオブジェクトを直接呼び出すことができるメソッドを指します

多形の使い方

classAnimal:
 def talk(self):
  pass

classPeople(Animal):
 def talk(self):print('say hello')classDog(Animal):
 def talk(self):print('吠えている')classcat(Animal):
 def talk(self):print('ニャーニャーニャー')
peo1=People()
dog1=Dog()
cat1=cat()

# 特定のタイプを考慮せずに、オブジェクトのメソッドを直接呼び出す
peo1.talk()
dog1.talk()
cat1.talk()"""
次に、車のブランドがたくさんあるかどうかを考えます。そのブランドの車を運転する方法を学ぶ必要がありますか?
"""

# 以前は多態性が使用されており、オブジェクトの特定のタイプを考慮せずに、オブジェクトのメソッドが直接呼び出されます。
l =list([1,2,3])
s =str('hello')
t =tuple((4,5,6))

l.__len__()
s.__len__()
t.__len__() #これらの3つの特定のタイプを考慮する必要はありません。コンテナタイプである限り、lenメソッドを呼び出すことができます。
classAnimal:
 def talk(self):
  pass

classPeople(Animal)
 def jian(self):print('say hello')classDog(Animal):
 def han(self):print('吠えている')classCat(Animal):
 def hou(self):print('ニャーニャーニャー')
# 多態的なパフォーマンスを実現するための条件は、親クラスが子クラスの標準を定義し、動物が呼び出すことができ、呼び出しメソッドがトークである必要があることです。
# しかし、サブクラスがこのメソッドを呼び出さなければならないと言うように制限できますか?

# サブクラスは親クラスで定義された基準に従わなければならないと言える状況はありますか?
import abc
classAnimal(metaclass=abc.ABCMeta): #親クラスの存在の意味は、仕様を定義することです
 @ abc.abstractmethod
 def talk(self):
  pass
# Aoimal()抽象基本クラスはインスタンス化できません()classPeople(Animal):
 def jian(self):print('say hello')classDog(Animal):
 def han(self):print('吠えている')classCat(Animal);
 def hou(self):print('ニャーニャーニャー')
# その上で3つのクラスを披露すると、インスタンス化するとエラーが発生します
# しかし、Pythonは自由を促進し、単純さはプログラマーの開発を制限したくない
classPeople:
 def talk(self):print('say hello')classDog:
 def talk(self):print('吠えている')classCat:
 def talk(self):print('ニャーニャーニャー')
# Linuxをもう一度見ると、すべてがファイルです。
classDisk:
 def read(self):print('disk read')
 def write(self):print('disk write')classProcess:
 def read(self):print('processes')
 def write(self):print('processes write')classMemory:
 def read(self):print('memory read')
 def write(self):print('memory write')
オブジェクト情報を取得する#####

まず、type()関数を使用して、オブジェクトタイプを判別しましょう。

# 基本タイプが使用できます`type()`判定:

>>> type(123)<class'int'>>>>type('str')<class'str'>>>>type(None)<type(None)'NoneType'>

# 変数が関数またはクラスを指している場合は、次を使用することもできます。`type()`判定:
>>> type(abs)<class'builtin_function_or_method'>>>>type(a)<class'__main__.Animal'>>>>type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False

**isinstance()**を使用する

クラスの継承関係については、type()を使用するのは非常に不便です。クラスのタイプを判別するには、isinstance()関数を使用できます。
継承関係は次のとおりであると想定しています。

# Object --> Animal  --> Dog  --> Husky
# 次に,isinstance()オブジェクトが特定のタイプを持っているかどうかを教えてくれます,まず、3種類のオブジェクトを作成します:
# isinstance()判断されるのは、オブジェクトがタイプ自体であるか、タイプの親継承チェーンにあるかどうかです。.
# type()基本的な判定の種類もisinstanceを使用できます()判定.

dir()

# オブジェクトのすべての属性とメソッドを取得する場合は、dirを使用できます。()関数、彼は文字列を含むリストを返します,といった、
# strオブジェクトのすべての属性とメソッドを取得します.
str ='ABC'print(type(str))print(dir(str))

len

# 同様`__xxx__`の属性とメソッドは、Pythonの特別な目的で使用されます。`__len__`このメソッドは長さを返します。
# Pythonでは、`len()`この関数は、実際には、オブジェクトの長さを取得しようとします。`len()`関数内では、
# オブジェクトを自動的に呼び出します`__len__()`メソッドなので、次のコードは同等です。
>>> len('ABC')3>>>'ABC'.__len__()3
# lenも使用したい場合は、自分で作成したクラス(myObj)次に、自分で作成します__len__()方法.classMyDog(object):
 def __len__(self):return100

dog =MyDog()print(len(dog))

lower()

# いくつかの一般的な属性またはメソッドがあります,下など()小文字の文字列を返す.>>>'ABC'.lower()'abc'

getattr,setattr,hasattr

# プロパティとメソッドをリストするだけでは不十分です,getattrで(),setattr()そしてhasattr(),オブジェクトの状態を操作できます.classMyObject(object):
 def __init__(self):
  self.x =9
 def power(self):return self.x * self.x

obj =MyObject()print(obj.power())
# 次に、オブジェクトのプロパティをテストできます
print(hasattr(obj,'x'))print(obj.x)setattr(obj,'y',19)print(hasattr(obj,'y'))print(obj.y)
# 存在しない属性を取得すると、AttributeErrorがスローされます.
# デフォルトのパラメータを渡すことができます。属性が存在しない場合は、デフォルト値を返します
print(getattr(obj,'z',404))

# オブジェクトを取得する方法
print(hasattr(obj,'power')) #属性があります'power'それは...ですか
print(getattr(obj,'power')) #属性を取得する'power'
fn =getattr(obj,'power')   #属性を取得する'power'そして変数fnに割り当てます
print(fn())print(fn)

概要

一連の組み込み関数を使用して、任意のPythonオブジェクトを分析し、その内部データを取得できます。オブジェクト情報がわからない場合にのみオブジェクト情報を取得することに注意してください。直接書き込むことができる場合:

sum = obj.x + obj.y
# 書かないでください
sum =getattr(obj,'x')+getattr(obj,'y')
# 正しい例は次のとおりです
def readImage(fp):ifhasattr(fp,'read'):returnreadData(fp)return  None
# ファイルストリームfpからイメージを読み取りたい場合、最初にfpオブジェクトにreadメソッドがあるかどうかを判断する必要があります。存在する場合、オブジェクトはストリームです。
# 存在しない場合は読み取ることができません。 hasattr()重宝しました。

# Pythonのような動的言語では、アヒルの種類に応じて、次のように読み取られることに注意してください()メソッドは、fpオブジェクトがファイルストリームであることを意味するのではなく、
# ネットワークストリームの場合もあります,メモリ内のバイトストリームの場合もあります,しかし、ただ読んでください()このメソッドは有効な画像データを返します,画像の読み取り機能には影響しません
インスタンス属性とクラス属性#####

Pythonは動的言語であるため、クラスによって作成されたインスタンスは任意の属性にバインドできます。
プロパティをインスタンスにバインドする方法は、インスタンス変数または自己変数を使用します。

classStudent(object):
 def __init__(self,name):
  self.name = name

s =Student('Alice')
s.score =90>しかし、`Student`クラス自体がプロパティをバインドする必要がありますか?クラスで直接属性を定義できます。
# この属性はクラス属性であり、`Student`クラスの所有権:
classStudent(object):
 name ='Student'>クラス属性を定義すると、この属性は分類されますが、クラスのすべてのインスタンスにアクセスできます。それをテストしましょう:

classStudent(object):
 name ='Student'

s =Student()print(s.name)print(Student.name)

s.name ='YouMen'print(s.name)   #インスタンス属性はクラス属性よりも優先されるため、クラスの名前属性をブロックします

del s.name  #インスタンスのname属性を削除した場合
print(s.name)   #もう一度呼び出す.name,インスタンスのName属性が見つからないため、クラスの属性が表示されます

上記の例からわかるように、プログラムを作成するときは、インスタンス属性とクラス属性に同じ名前を使用しないでください。同じ名前のインスタンス属性はクラス属性を保護しますが、インスタンス属性を削除するときは、もう一度使用してください。同じ名前の場合、アクセスはクラス属性になります。

概要

# インスタンス属性は各インスタンスに属し、相互に干渉しません
# クラス属性はクラスに属し、すべてのインスタンスが属性を共有します
# インスタンス属性とクラス属性に同じ名前を使用しないでください。同じ名前を使用すると、見つけにくいエラーが発生します。.

クラスのデコレータ####

property

# 体のBMIインデックス
"""
成人のBMI値:
軽すぎる:18未満.5
通常:18.5-23.9
オーバーウェイト:24-27
肥満:28-32
非常に肥満,32より高い
ボディマスインデックス(BMI)=重量(kg)/高さ^2(m)
  EX:70kg÷(1.75×1.75)=22.86"""

classPeople:
 def __init__(self,name,height,weight):
  self.name=name
  self.height=height
  self.weight=weight
 @ property
 def bmi(self):return self.weight /(self.height **2)

youmen=People('YouMen',1.78,55)
youmen.height=1.78print(youmen.bmi)

理解するために

classPeople:
 def __init__(self,name):
  self.__name=name

 @ property
 def name(self):return self.__name

 @ name.setter
 def name(self,va1):
  # print('====>名前の値を変更する準備ができました:',val)iftype(va1) is not str:
   raise TypeError('名前の値はタイプstrである必要があります')
  self.__name=val
    
 @ name.deleter
 def name(self):
  # def self.__name
  print('削除させないで、兄弟滞在')
# classmethod
# staticmethod

反射####

リフレクションとは

リフレクションの概念は、1982年にスミスによって最初に提案されました。これは主に、プログラムが自身の状態または動作にアクセス、検出、および変更する機能(イントロスペクション)を指します。この概念の提案は、すぐにコンピュータサイエンスの分野での応用反射に関する研究につながりました。プログラミング言語の設計分野で最初に採用され、Lispおよびオブジェクト指向で成果を上げました。

**Pythonオブジェクト指向リフレクション:文字列の形式でオブジェクト関連の属性を操作します。Pythonのすべてがオブジェクトです(リフレクションを使用できます)**文字列を介してクラスまたはオブジェクトの属性またはメソッドを取得します

リフレクション:文字列を介してクラスまたはオブジェクトのプロパティを操作することを指します

classPeople:
 country='China'
 def __init__(self,name):
  self.name=name
obj=People('YouMen')

# 4つの組み込み機能が含まれています
# hasattr
print('country'in People.__dict__)print(hasattr(People,'country'))

# getattr
print(People.__dict__['country'])print(getattr(People,'country'))print(getattr(People,'country1111',None))

# delattr
delattr(People,'country')print(People.__dict__)

応用

classftp:
 def get(self):print('get...')

 def put(self):print('put...')

 def auth(self):print('auth...')
    
 def run(self):while True:
   cmd=input('>>>:').strip() # cmd='get'ifhasattr(self,cmd):
    method =getattr(self,cmd)method()else:print('出力方法が存在しません')
obj=ftp()
obj.run()

オブジェクト指向の高度なプログラミング####

__slots __ #####を使用します

通常の状況では、クラスを定義してクラスのインスタンスを作成するときに、任意のプロパティとメソッドをインスタンスにバインドできます。これは、動的言語の柔軟性です。

classStudent(object):
 pass

# 次に、プロパティをインスタンスにバインドしてみます
s =Student()
s.name ='Michael'  #プロパティをインスタンスに動的にバインドします
print(s.name)

# メソッドをインスタンスにバインドすることもできます:from types import MethodType

def set_age(self,age):
 self.age = age

s.set_age =MethodType(set_age,s)   #メソッドをインスタンスにバインドします
s.set_age(25)print(s.age)

# ただし、あるインスタンスにバインドされたメソッドは、別のインスタンスでは機能しません.>>> s2 =Student() #新しいインスタンスを作成します
>>> s2.set_age(25) #メソッドを呼び出してみてください
Traceback(most recent call last):
 File "<stdin>", line 1,in<module>
AttributeError:'Student' object has no attribute 'set_age'

# メソッドをすべてのインスタンスにバインドするために、メソッドをクラスにバインドできます.from types import MethodType
def set_score(self,score):
 self.score=score
 return score

Student.set_score = set_score
# メソッドをクラスにバインドした後、すべてのインスタンスを呼び出すことができます:print(s.set_score(34))

通常、上記のset_scoreメソッドはクラスで直接定義できますが、動的バインディングを使用すると、プログラムの実行中にクラスに関数を動的に追加できます。これは、静的言語では実現が困難です** __sllots __ **を使用します。

しかし、インスタンスの属性を制限したい場合はどうなりますか?たとえば、名前と年齢の属性のみをStudentインスタンスに追加できるようにします。
制限の目的を達成するために、Pythonでは、クラスを定義するときに特別な__slots__変数を定義して、クラスインスタンスに追加できる属性を制限することができます。

classStudent(object):
 __ slots__ =('name','age')

# 次に、
s =Student()   #新しいインスタンスを作成します
s.name ='Michael'  #バインディングプロパティ'name'
s.age =25      #バインディング属性の年齢
# s.score =99    #バインドされた属性スコア

'score'は__slots__に配置されていないため、 score属性をバインドできません。scoreをバインドしようとすると、 AttributeErrorのエラーが発生します。
__slots__を使用する場合、 __slots__で定義された属性は現在のクラスインスタンスにのみ影響し、継承されたサブクラスには影響しないことに注意してください。

>>> classGraduateStudent(Student):...     pass
...>>> g =GraduateStudent()>>> g.score =9999

__slots__もサブクラスで定義されていない限り、このように、サブクラスインスタンスに許可される属性は、それ自体の __slots__と親クラスの __slots__です。

@property #####を使用します

プロパティをバインドするときに、プロパティを直接公開すると、書くのは簡単ですが、パラメータを確認する方法がないため、結果を自由に変更できます。

s =Student()
s.score =9999

これは明らかに論理的ではありません。スコアの範囲を制限するには、set_score()メソッドを使用してスコアを設定し、get_score()を使用してスコアを取得します。これにより、set_score()メソッドでパラメーターを確認できます。

classStudent(object):

 def get_score(self):return self.__score

 def set_score(self,value):if not isinstance(value,int):
   raise ValueError('score must be an integer')if value <0 or value >100:
   raise ValueError('score must between 0 ~ 100')
  self.__score = value
# これで、Studentインスタンスを操作する場合、希望どおりにスコアを設定することはできません。
s =Student()
s.set_score(60)print(s.get_score())
# s.set_score(1000)
# print(s.get_score())
カスタムクラス#####

__xxx__の形式の__slots__のような変数または関数名を見つけた場合は、それらに注意する必要があります。これらはPythonで特別に使用されます。
__ slot__の使用方法はすでに知っています。また、 __len __()メソッドはクラスが len()関数に作用できるようにすることも知っています。
さらに、Pythonクラスにはそのような特別な目的の関数がたくさんあり、クラスのカスタマイズに役立ちます。

**str **最初にStudentクラスを定義し、インスタンスを出力します:

classStudent(object):
 def __init__(self,name):
  self.name = name

print(Student('YouMen'))<__main__.Student object at 0x018EAEF0>

# この住所の値を印刷するのはあまり美しくないと思います,見栄えの良いネパール語を印刷する方法,定義する必要があります__str__()メソッド、素敵な文字列を返すだけ.classStudent(object):
 def __init__(self,name):
  self.name = name
 def __str__(self):return'Student object (name: %s)'% self.name
print(Student('YouMen'))
Student object(name: YouMen)
# このように印刷された例は、見栄えが良いだけでなく、例内の重要なデータを簡単に確認できます。.
# しかし、印刷しないと印刷例はまだ良くないことがわかります.
# これは、直接表示変数が呼び出されないためです。__str__(),だが__repr__()ユーザーに表示される文字列を返します。
# そして__repr__()プログラム開発者が見た文字列を返します。つまり、,__repr__()デバッグ用です

# 解決策は別のものを定義することです__repr__(),しかし、通常は__str__()と__repr__()コードは同じなので、怠惰な書き方があります。
classStudent(object):
 def __init__(self, name):
  self.name = name
 def __str__(self):return'Student object (name=%s)'% self.name
 __ repr__ = __str__

iter

リストやタプルと同様に、クラスを for ... inループで使用する場合は、反復オブジェクトを返す__iter __()メソッドを実装する必要があります。そうすると、Pythonforループは引き続き呼び出します。この反復オブジェクトの __next __()メソッドは、 StopIterationエラーが発生したときにループを終了するまで、ループの次の値を取得します。

例としてFibonacciシーケンスを取り上げ、forループで使用できるFibクラスを記述しましょう:

classFib(object):
 def __init__(self):
  self.a,self.b =0,1 #2つのオブジェクトを初期化します

 def __iter__(self):return self         #インスタンス自体は反復オブジェクトなので、それ自体に戻ります

 def __next__(self):
  self.a,self.b = self.b,self.a + self.b #次の値を計算する
  if self.a >10000: #ループを終了するための条件
   raise StopAsyncIteration()return self.a       #次の値を返す.for i inFib():print(i)

getitem

Fibインスタンスはforループで使用できますが、リストに少し似ていますが、リストとして使用することはできません。たとえば、5番目の要素を考えてみましょう。

classFib(object):
 def __getitem__(self, i):
  a,b=1,1for x inrange(i):
   a,b = b,a+b
  return x

a=Fib()print(a.__getitem__(324))
# これで、インデックスを押すことでシリーズの任意のアイテムにアクセスできます.

# しかし、リストのようにスライスしたい場合,しかし、このフィブはエラーを報告しました
classFib(object):
 def __getitem__(self, n):ifisinstance(n, int): #nはインデックスです
   a, b =1,1for x inrange(n):
    a, b = b, a + b
   return a
  ifisinstance(n, slice): #nはスライスです
   start = n.start
   stop = n.stop
   if start is None:
    start =0
   a, b =1,1
   L =[]for x inrange(stop):if x >= start:
     L.append(a)
    a, b = b, a + b
   return L
f =Fib()print(f[0:5])

getattr

通常の状況では、クラスのメソッドまたはプロパティを呼び出すときに、それが存在しない場合、エラーが報告されます。たとえば、 Studentクラスを定義します。

classStudent(object):
    
 def __init__(self):
  self.name ='Michael'

# 転送`name`属性,没问题,但是,転送不存在的`score`プロパティ、問題があります:
# エラーメッセージは、それが見つからなかったことを明確に示しています`score`この属性。
# このエラーを回避するには、`score`属性に加えて、Pythonには別のメカニズムがあります。
# それは1つを書くことです`__getattr__()`属性を動的に返すメソッド。以下のように修正します
classStudent(object):
 def __init__(self):
  self.name ='YouMen'

 def __getattr__(self, attr):if attr =='score':return404

s =Student()print(s.name)print(s.score)

# リターン機能も完全に可能です.classStudent(object):
 def __init__(self):
  self.name ='YouMen'

 def __getattr__(self, sttr):if sttr =='age':return lambda:25

s =Student()print(s.name)print(s.score)

# 属性が見つからない場合にのみ呼び出されることに注意してください`__getattr__`,などの既存の属性`name`,なりません`__getattr__`で見つける
# さらに、次のような任意の呼び出しに注意してください`s.abc`戻ります`None`、これは私たちが定義したためです`__getattr__`デフォルトのリターンは`None`。
# クラスがいくつかの特定の属性のみに応答するようにするには、規則に従ってスローする必要があります`AttributeError`間違い:
classStudent(object):
 def __init__(self):
  self.name ='YouMen'

 def __getattr__(self, sttr):if sttr =='age':return lambda:25
  raise AttributeError('\'Student\' object bas no attribute \'%s\''% sttr)

a =Student()print(a.sttr)

実際、クラスのすべての属性とメソッド呼び出しは、特別な手段なしで動的に処理できます。
この完全に動的な呼び出し機能の実際的な効果は何ですか?その結果、完全に動的な状況を呼び出すことができます。

例えば:
現在、多くのWebサイトでSinaWeiboやDoubanなどのRESTAPIが使用されています。APIを呼び出すためのURLは次のとおりです。
SDKを作成し、各URLに対応するAPIにメソッドを作成する場合、それは使い果たされ、APIが変更されたら、SDKを変更する必要があります。
完全に動的な __getattr__を使用して、チェーン呼び出しを記述できます。

classChain(object):
 def __init__(self,path=''):
  self.__path = path

 def __getattr__(self,path):returnChain('%s/%s'%(self.__path,path ))

 def __str__(self):return self.__path
 __ repr__ = __str__

a =Chain()print(a.status.user.timeline.list)/status/user/timeline/list

# このように、APIがどのように変更されても、SDKはURLに基づいて完全に動的な呼び出しを実装でき、APIの増加に伴って変更されることはありません。
# GitHub APIなど、URLにパラメータを配置するRESTAPIもいくつかあります。
` GET /users/:user/repos`
# 電話をかけるときは、`:user`実際のユーザー名に置き換えてください。このようなチェーンコールを記述できる場合:
` Chain().users('michael').repos`
# APIを非常に便利に呼び出すことができます。興味のある子供用の靴は、それを書き出すことを試みることができます。

call

オブジェクトインスタンスは、独自のプロパティとメソッドを持つことができます。インスタンスメソッドを呼び出すときは、 instance.method()を使用して呼び出します。インスタンス自体で直接呼び出すことはできますか? Pythonでは、答えは「はい」です。
どのクラスでも、 __call __()メソッドを定義するだけで、インスタンスを直接呼び出すことができます。例を見てください:

classStudent(object):
 def __init__(self,name):
  self.name = name

 def __call__(self):print('My name is %s.'% self.name)

s =Student('YouMen')s()		#自己パラメータを渡さないでください

__ call __() はパラメータを定義することもできます。インスタンスを直接呼び出すことは、関数を呼び出すことに似ています。したがって、2つの間に基本的な違いがないため、オブジェクトを関数として、関数をオブジェクトとして考えることができます。

オブジェクトを関数と考えると、クラスのインスタンスは実行時に作成されるため、関数自体は実際には実行時に動的に作成できます。このようにして、オブジェクトと関数の境界をぼかします。

では、変数がオブジェクトであるか関数であるかを判断するにはどうすればよいでしょうか。実際、多くの場合、オブジェクトを呼び出すことができるかどうかを判断する必要があります。呼び出すことができるオブジェクトは、関数や上記で定義された __call __()を持つクラスインスタンスなどの Callableオブジェクトです。

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1,2,3])
False
>>> callable(None)
False
>>> callable('str')
False

callable()関数を使用して、オブジェクトが「呼び出し可能」オブジェクトであるかどうかを判別できます。

列挙クラスを使用する#####

定数を定義する必要がある場合、1つの方法は、大文字の変数を使用して月などの整数を定義することです。

JAN =1  
FEB =2  
MAR =3  
  
NOV =11  
DEC =12

利点は単純さですが、欠点は型がintであり、それでも変数であるということです。
より良い方法は、そのような列挙型のクラス型を定義することです。そうすると、各定数はクラスの一意のインスタンスになります。Pythonは、この機能を実現するためにEnumクラスを提供します。

fromenumimport Enum  
Month =Enum('Month',('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug'))  
  
# だから私たちは得る`Month`列挙クラスの種類、直接使用できます`Month.Jan`定数を引用するには、
# または、そのすべてのメンバーを列挙します。
  
for name, member in Month.__members__.items():print(name,'=>', member,',', member.value)  
  
# value属性は、メンバーに自動的に割り当てられるint定数です。,カウントはデフォルトで1から始まります。
# 列挙型をより正確に制御する必要がある場合は、Enumからカスタムクラスを派生させることができます.
fromenumimport Enum,unique  
  
@ unique  
classWeekday(Enum):  
 Sum =0 #Sumの値は0に設定されます
 Mon =1  
 Tue =2  
 Wed =3  
 Thu =4  
 Fri =5  
 Sat =6  
# @ 独自のデコレータは、重複する値がないことを確認するのに役立ちます
# これらの列挙型にアクセスする方法はいくつかあります:  
day1 = Weekday.Mon  
print(day1)print(Weekday.Sat)print(Weekday(1))for name, member in Weekday.__members__.items():print(name,'=>', member)  
# コースウェアでは、メンバー名を使用して列挙定数を参照でき、値の値に応じて列挙定数を直接取得できます。.

Example1

fromenumimport Enum,unique  
classGender(Enum):  
 Male =0  
 Female =1classStudent(object):  
 def __init__(self,name,gender):  
 self.name = name  
 self.gender = gender  
bart =Student('Bart',Gender.Male)if bart.gender == Gender.Male:print("テストに合格")else:print('テストに失敗しました')

概要

Enumは、クラス内の関連する定数のセットを定義でき、クラスは不変であり、メンバーを直接比較できます。

メタクラス#####を使用します

type

動的言語と静的言語の最大の違いは、関数とクラスの定義がコンパイル時に定義されるのではなく、実行時に動的に作成されることです。

たとえば、Helloクラスを定義する場合は、hello.pyモジュールを記述します。

classHello(object):
 def hello(self, name='world'):print('Hello, %s.'% name)

Pythonインタープリターが helloモジュールをロードすると、モジュールのすべてのステートメントが順番に実行されます。実行の結果、 Helloのクラスオブジェクトが動的に作成されます。テストは次のとおりです。

cat hello.py 
#! /usr/bin/python3
classHello(object):
	def hello(self,name='world'):print('Hello,%s'% name)

cat type_demo.py 
#! /usr/bin/python3
from hello import Hello
h =Hello()
h.hello()print(type(h))

python3 type_demo.py 
Hello,world
< class'hello.Hello'>
# tyep()関数は、タイプまたは変数のタイプを表示できます。`Hello`クラスであり、そのタイプは`type`,
# そして`h`インスタンスであり、そのタイプはクラスです`Hello`。

# クラスの定義は実行時に動的に作成されると言います。クラスを作成する方法は、`type()`関数。

# ` type()`関数はオブジェクトのタイプを返すことができ、たとえば、次のような新しいタイプを作成できます。
# 合格できます`type()`作成された関数`Hello`合格せずに授業`class Hello(object)...`定義:

クラスオブジェクトを作成するために、type()関数は3つのパラメーターを順番に渡します。

1. クラスの名前。
2. 継承された親クラスのコレクション。Pythonは複数の継承をサポートしていることに注意してください。親クラスが1つしかない場合は、タプルの単一要素の記述を忘れないでください。
3. クラスのメソッド名は関数にバインドされています。ここに関数を配置します`fn`メソッド名にバインド`hello`オン。

type()関数によって作成されたクラスは、クラスを直接書き込むのとまったく同じです。これは、Pythonインタープリターがクラス定義に遭遇すると、
クラス定義の構文をスキャンしてから、 type()関数を呼び出してクラスを作成するだけです。

通常は class Xxx ...を使用してクラスを定義しますが、 type()関数を使用すると、クラスを動的に作成することもできます。つまり、動的言語自体が実行時のクラスの動的作成をサポートします。静的言語とは大きく異なります。静的言語の実行時にクラスを作成するには、ソースコード文字列を作成してからコンパイラを呼び出すか、いくつかのツールを使用してバイトコードを生成する必要があります。本質的に、これは動的コンパイルであり、非常に複雑です。

metaclass

type()を使用してクラスを動的に作成することに加えて、クラスの作成動作を制御するために、メタクラスを使用することもできます。

メタクラス、文字通りメタクラスとして翻訳された、簡単な説明は次のとおりです。

クラスを定義した後、このクラスに基づいてインスタンスを作成できます。つまり、最初にクラスを定義してから、インスタンスを作成します。
しかし、クラスを作成したい場合はどうでしょうか。次に、メタクラスに基づいてクラスを作成する必要があるため、最初にメタクラスを定義してから、クラスを作成します。
接続は次のとおりです。最初にメタクラスを定義し、次にクラスを作成し、最後にインスタンスを作成します。
したがって、メタクラスを使用すると、クラスを作成または変更できます。つまり、クラスはメタクラスによって作成された「インスタンス」と考えることができます。
メタクラスは、Pythonオブジェクト指向で理解するのが最も難しく、魔法のコードを使用するのも最も困難です。通常はメタクラスを使用する必要はありませんので、基本的には使用しないので、以下の内容がわからなくても構いません。
最初に簡単な例を見てみましょう。このメタクラスは、カスタムMyListに addメソッドを追加できます。

ListMetaclassを定義します。デフォルトの習慣によれば、メタクラスのクラス名は、これがメタクラスであることを明確に示すために、常にメタクラスで終わります。

# メタクラスはクラスのテンプレートであるため、`type`タイプの派生:
classListMetaclass(type):
 def __new__(cls, name, bases, attrs):
  attrs['add']= lambda self, value: self.append(value)return type.__new__(cls, name, bases, attrs)

ListMetaclassでは、クラスを定義するときに、ListMetaclassを使用してクラスをカスタマイズし、キーワードパラメーター metaclassを渡すように指示します。

classMyList(list, metaclass=ListMetaclass):
 pass

キーワードパラメータ metaclassを渡すと、魔法が有効になります。これは、Pythonインタープリターに、クラスの定義を変更できるListMetaclass .__ new __()を介して MyListを作成するように指示します。 、たとえば、新しいメソッドを追加してから、改訂された定義に戻ります。
__ new __() メソッドが受け取るパラメーターは次のとおりです。

  1. 現在作成されるクラスのオブジェクト。
  2. クラスの名前。
  3. クラスによって継承された親クラスのコレクション。
  4. クラスのメソッドのコレクション。

MyListadd()メソッドを呼び出すことができるかどうかをテストします。

>>> L =MyList()>>> L.add(1)>> L
[1]

また、通常の listにはadd()メソッドがありません。

>>> L2 =list()>>> L2.add(1)Traceback(most recent call last):
 File "<stdin>", line 1,in<module>
AttributeError:'list' object has no attribute 'add'

動的変更の重要性は何ですか? MyListの定義に直接add()メソッドを書く方が簡単ではないでしょうか?通常の状況では、直接書き込む必要があり、メタクラスによる変更は純粋に異常です。
ただし、メタクラスを介してクラス定義を変更する必要が常に発生します。 ORMは典型的な例です。

ORMのフルネームは「ObjectRelationalMapping」、つまり、リレーショナルデータベースの行をオブジェクトにマッピングするオブジェクトリレーショナルマッピングです。つまり、クラスはテーブルに対応します。このように、SQLステートメントを直接操作せずにコードを記述しやすくなります。
ORMフレームワークを作成するには、テーブルの構造に従って対応するクラスを定義できるのはユーザーのみであるため、すべてのクラスを動的に定義することしかできません。

ORMフレームワークを書いてみましょう。
低レベルモジュールを作成する最初のステップは、最初に呼び出し側インターフェイスを作成することです。たとえば、ユーザーがこのORMフレームワークを使用していて、対応するデータベーステーブル Userを操作するために Userクラスを定義したい場合、次のコードを作成する必要があります。

classUser(Model):
 # クラス属性の列へのマッピングを定義します。
 id =IntegerField('id')
 name =StringField('username')
 email =StringField('email')
 password =StringField('password')

# インスタンスを作成します。
u =User(id=12345, name='Michael', email='[email protected]', password='my-pwd')
# データベースに保存:
u.save()

その中で、親クラス Modelと属性タイプ StringFieldおよび IntegerFieldはORMフレームワークによって提供され、save()などの残りの魔法のメソッドはすべてメタクラスによって自動的に完了します。メタクラスの準備はより複雑になりますが、ORMのユーザーは非常に簡単に使用できます。
次に、上記のインターフェイスに従ってORMを実装します。
まず、データベーステーブルのフィールド名とフィールドタイプの保存を担当するFieldクラスを定義します。

classField(object):

 def __init__(self, name, column_type):
  self.name = name
  self.column_type = column_type

 def __str__(self):return'<%s:%s>'%(self.__class__.__name__, self.name)

Fieldに基づいて、StringField、IntegerFieldなどのさまざまなタイプのフィールドをさらに定義します。

classStringField(Field):

 def __init__(self, name):super(StringField, self).__init__(name,'varchar(100)')classIntegerField(Field):

 def __init__(self, name):super(IntegerField, self).__init__(name,'bigint')

次のステップは、最も複雑なModelMetaclassを作成することです。

classModelMetaclass(type):

 def __new__(cls, name, bases, attrs):if name=='Model':return type.__new__(cls, name, bases, attrs)print('Found model: %s'% name)
  mappings =dict()for k, v in attrs.items():ifisinstance(v, Field):print('Found mapping: %s ==> %s'%(k, v))
    mappings[k]= v
  for k in mappings.keys():
   attrs.pop(k)
  attrs['__mappings__']= mappings #属性と列の間のマッピング関係を保存します
  attrs['__table__']= name #テーブル名とクラス名が同じであると仮定します
  return type.__new__(cls, name, bases, attrs)

そして基本クラスのモデル

classModel(dict, metaclass=ModelMetaclass):

 def __init__(self,**kw):super(Model, self).__init__(**kw)

 def __getattr__(self, key):try:return self[key]
  except KeyError:
   raise AttributeError(r"'Model' object has no attribute '%s'"% key)

 def __setattr__(self, key, value):
  self[key]= value

 def save(self):
  fields =[]
  params =[]
  args =[]for k, v in self.__mappings__.items():
   fields.append(v.name)
   params.append('?')
   args.append(getattr(self, k, None))
  sql ='insert into %s (%s) values (%s)'%(self.__table__,','.join(fields),','.join(params))print('SQL: %s'% sql)print('ARGS: %s'%str(args))

ユーザーが class User(Model)を定義すると、Pythonインタープリターは最初に現在のクラス Userの定義で metaclassを探します。見つからない場合は、親クラス Model metaclassを検索し続け、次に、 Modelで定義された metaclassModelMetaclassを使用して Userクラスを作成します。つまり、メタクラスは暗黙的にサブクラスに継承できますが、サブクラス自体はそれを感知できません。

ModelMetaclassでは、合計でいくつかのことが行われます。

  1. Modelクラスへの変更を除外します。
  2. 現在のクラスで定義されているクラスのすべての属性( Userなど)を検索します。Field属性が見つかった場合は、それを __mappings__の辞書に保存し、class属性からField属性を削除します。それ以外の場合は簡単です。ランタイムエラーが発生します(インスタンスの属性は、クラスの同じ名前の属性をカバーします)。
  3. テーブル名を __table__に保存します。これはテーブル名に簡略化され、デフォルトではクラス名になります。

Modelクラスでは、save()delete()find() updateなど、データベースを操作するさまざまなメソッドを定義できます。
データベースにインスタンスを保存するために save()メソッドを実装しました。テーブル名、属性からフィールドへのマッピング、および属性値の収集により、INSERTステートメントを作成できます。

コードを書いてみてください:

u =User(id=12345, name='Michael', email='[email protected]', password='my-pwd')
u.save()

出力は次のとおりです。

Found model: User
Found mapping: email ==><StringField:email>
Found mapping: password ==><StringField:password>
Found mapping: id ==><IntegerField:uid>
Found mapping: name ==><StringField:username>
SQL: insert into User(password,email,username,id)values(?,?,?,?)
ARGS:['my-pwd','[email protected]','Michael',12345]

ご覧のとおり、 save()メソッドは実行可能なSQLステートメントとパラメーターリストを出力しました。実際の関数を完了するには、データベースに接続してSQLステートメントを実行するだけです。
100行未満のコードで、メタクラスを介して合理化されたORMフレームワークを実装しました

概要

メタクラスはPythonの非常に魔法のオブジェクトであり、作成時にクラスの動作を変更できます。この強力な機能は注意して使用してください。

Recommended Posts

10.オブジェクト指向のPython
1分でPythonを学ぶ|オブジェクト指向(パート1)
Python反復可能オブジェクトの重複排除の例
Pythonのすべてがオブジェクトです
Pythonマルチスレッド
Python CookBook
Python FAQ
Python3辞書
Python3モジュール
python(you-get)
Python文字列
Python記述子
Pythonの基本2
Pythonノート
Python3タプル
CentOS + Python3.6 +
Python Advanced(1)
Pythonデコレータ
Python IO
Pythonマルチスレッド
Pythonツールチェーン
Python3リスト
Pythonマルチタスク-日常
Pythonの概要
pythonの紹介
Pythonアナリティック
Pythonの基本
07.Python3関数
pythonインスタンス化オブジェクトの特定のメソッド
Pythonの基本3
Pythonマルチタスクスレッド
Python関数
python sys.stdout
python演算子
Pythonエントリ-3
Centos 7.5 python3.6
Python文字列
Pythonの基本4
Pythonの基本5