オブジェクト向けプログラミングベース-Phythonクラス(一)

12325 ワード

pythonでのインスタンス属性の作成
Personクラスでxiaoming、xiaohongなどのインスタンスを作成できますが、これらのインスタンスはアドレスの違い以外に何の違いもありません.現実世界では、xiaoming、xiaohongを区別するには、それぞれの名前、性別、誕生日などの属性に頼る必要があります.
各インスタンスに異なるプロパティを持たせるにはどうすればいいですか?Pythonは動的言語であるため、各インスタンスに直接属性を割り当てることができます.たとえば、xiaomingというインスタンスにname、gender、birth属性を加えることができます.
    xiaoming = Person()
    xiaoming.name = 'Xiao Ming'
    xiaoming.gender = 'Male'
    xiaoming.birth = '1990-1-1'

xiaohongに加えたプロパティは、必ずしもxiaomingと同じではありません.
xiaohong = Person()
xiaohong.name = 'Xiao Hong'
xiaohong.school = 'No. 1 High School'
xiaohong.grade = 2

インスタンスのプロパティは、通常の変数のように操作できます.
xiaohong.grade = xiaohong.grade + 1

列:
2つのPersonクラスを含むインスタンスのリストを作成し、2つのインスタンスのnameに値を割り当て、nameに従ってソートします.
    class Person(object):
        pass
    
    p1 = Person()
    p1.name = 'Bart'
    
    p2 = Person()
    p2.name = 'Adam'
    
    p3 = Person()
    p3.name = 'Lisa'
    
    L1 = [p1, p2, p3]
    L2 = sorted(L1,lambda p1,p2:cmp(p1.name,p2.name))
    
    print L2[0].name
    print L2[1].name
    print L2[2].name

pythonでインスタンス属性を初期化する
1つのインスタンスに様々な属性を自由にバインドすることができますが、現実世界では、1つのタイプのインスタンスが同じ名前の属性を持つべきです.たとえば、Personクラスは作成時にnamegenderbirthの属性を持つべきですが、どうすればいいですか?
Personクラスを定義する場合、Personクラスに特別なinit()メソッドを追加できます.インスタンスを作成すると、init()メソッドが自動的に呼び出され、インスタンスごとに次のプロパティを統一できます.
    class Person(object):
        def __init__(self, name, gender, birth):
            self.name = name
            self.gender = gender
            self.birth = birth
init()メソッドの最初のパラメータはselfでなければなりません(別の名前でもいいですが、習慣的な使い方を推奨します).後続のパラメータは自由に指定でき、定義関数と何の違いもありません.
したがって、インスタンスを作成するには、self以外のパラメータを指定する必要があります.
    xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
    xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')
init()メソッドにより、Personインスタンス毎にnamegenderbirthおよびinit()の3つの属性が作成する、また、異なる属性値が付与され、属性使用にアクセスする.オペレータ:
print xiaoming.name
#    'Xiao Ming'
print xiaohong.birth
#    '1992-2-2'

特に、初心者定義selfメソッドは、**kwパラメータを忘れがちであることに注意してください.
>>> class Person(object):
...     def __init__(name, gender, birth):
...         pass
... 
>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (most recent call last):
  File "", line 1, in 
TypeError: __init__() takes exactly 3 arguments (4 given)

これは、最初のパラメータnameがPythonインタプリタによってインスタンスの参照に渡され、メソッド全体の呼び出しパラメータの位置が一致しないため、作成に失敗したり、正常に動作しなかったりします.
例:
キーワードパラメータを定義するには、self.name = 'xxx';setattr(self, 'name', 'xxx')を直接使用して属性を設定できるほか、(__)を使用して属性を設定することもできます.
参照コード:
    class Person(object):
        def __init__(self, name, gender, birth, **kw):
            self.name = name
            self.gender = gender
            self.birth = birth
            for k, v in kw.iteritems():
                setattr(self, k, v)
    xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
    print xiaoming.name
    print xiaoming.job

pythonでのアクセス制限
Pythonの属性権限の制御は属性名によって実現され、1つの属性が二重下線の先頭"__job"である場合、その属性は外部からアクセスできない.例を見てみましょう.
class Person(object):
    def __init__(self, name):
        self.name = name
        self._title = 'Mr'
        self.__job = 'Student'
p = Person('Bob')
print p.name
# => Bob
print p._title
# => Mr
print p.__job
# => Error
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'Person' object has no attribute '__job'

二重下線で始まる"__xxx__"のみが外部に直接アクセスできないことがわかります.
ただし、属性が"__xxx__"の形式で定義されている場合は、外部からアクセスすることもできます."__xxx__"で定義された属性はPythonのクラスでは特殊な属性と呼ばれ、多くの事前定義された特殊な属性が使用できます.通常、一般的な属性を"_xxx"で定義しないでください.
下線で始まる属性Personは、外部からアクセスすることもできますが、習慣的には外部からアクセスするべきではありません.
例:
class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.__score = score

p = Person('Bob', 59)

print p.name
print p.__score

実行結果:
Traceback (most recent call last):
  File "index.py", line 9, in 
    print p.__score
AttributeError: 'Person' object has no attribute '__score'
Bob

pythonでのクラス属性の作成
クラスはテンプレートであり、インスタンスはクラスに基づいて作成されたオブジェクトです.
1つのインスタンスにバインドされたプロパティは他のインスタンスには影響しませんが、クラス自体もオブジェクトです.クラスにプロパティをバインドすると、すべてのインスタンスがクラスのプロパティにアクセスでき、すべてのインスタンスがアクセスするクラスのプロパティは同じです.すなわち、インスタンス属性は各インスタンスがそれぞれ持つ、互いに独立しており、クラス属性は1部のみである.
クラス属性を定義するには、classで直接定義します.
class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

クラス属性は直接クラスにバインドされているため、アクセスクラス属性はインスタンスを作成する必要がなく、直接アクセスできます.
print Person.address
# => Earth

1つのインスタンス呼び出しクラスのプロパティにもアクセスできます.すべてのインスタンスが属するクラスのプロパティにアクセスできます.
p1 = Person('Bob')
p2 = Person('Alice')
print p1.address
# => Earth
print p2.address
# => Earth

Pythonは動的言語であるため、クラス属性も動的に追加および変更できます.
Person.address = 'China'
print p1.address
# => 'China'
print p2.address
# => 'China'

クラス属性が1つしかないため、Personクラスのaddressが変更されると、すべてのインスタンスがアクセスするクラス属性が変更されます.
例:countクラスにクラス属性countを追加してください.インスタンスを作成するたびにPerson属性に1を追加します.これにより、合計何個__init__()のインスタンスが作成されたかを統計できます.インスタンスの作成には必ずcountメソッドが呼び出されるので、ここでクラス属性p1.address = 'China'を変更するのが適切です.
参照コード:
class Person(object):
    count = 0
    def __init__(self, name):
        Person.count = Person.count + 1
        self.name = name
p1 = Person('Bob')
print Person.count
# => 1
p2 = Person('Alice')
print Person.count
# => 2
p3 = Person('Tim')
print Person.count
# => 3

pythonのクラス属性とインスタンス属性の名前が衝突したらどうしますか?
クラス属性を変更すると、すべてのインスタンスがアクセスするクラス属性がすべて影響を受けますが、インスタンス変数でクラス属性を変更するとどのような問題が発生しますか?
class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

p1 = Person('Bob')
p2 = Person('Alice')

print 'Person.address = ' + Person.address

p1.address = 'China'
print 'p1.address = ' + p1.address

print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address

結果は次のとおりです.
Person.address = Earth
p1.address = China
Person.address = Earth
p2.address = Earth
__が設定された後、p 1アクセスaddressは確かに「China」になったが、Person.addressとp 2.addressはまだ「Earch」ですが、どうしたんですか.
原因はp 1である.address='China'はPersonのaddressを変更するのではなく、p 1というインスタンスにインスタンス属性addressをバインドします.p 1にはインスタンス属性address('China')があり、その属するクラスPersonにもクラス属性addressがあります.
アクセスp 1.addressの場合は、インスタンスプロパティを優先して「China」を返します.
p 2にアクセスする.addressの場合、p 2にはインスタンス属性addressはありませんが、クラス属性addressがあるため、「Earth」を返します.
インスタンス属性とクラス属性が重複すると、インスタンス属性の優先度が高くなり、クラス属性へのアクセスがブロックされます.
p 1のaddressインスタンス属性を削除すると、p 1にアクセスする.addressはまたクラス属性の値'Earth'を返します.
del p1.address
print p1.address
# => Earth

インスタンスにクラス属性を変更しないでください.実際にはクラス属性を変更するのではなく、インスタンスにインスタンス属性をバインドします.
列子:前節のPersonクラス属性countを__に変更してください.count、インスタンスとクラスからこのプロパティにアクセスできるかどうか試してみましょう.
class Person(object):
    __count = 0
    def __init__(self, name):
        Person.__count = Person.__count + 1
        self.name = name
        print Person.__count

p1 = Person('Bob')
p2 = Person('Alice')

print Person.__count

pythonでのインスタンスメソッドの定義
インスタンスのプライベート属性はselfで始まる属性で、外部からアクセスできません.これらの属性定義は何の役に立ちますか?
プライベート属性は外部からアクセスできませんが、クラスの内部からアクセスできます.インスタンスのプロパティを定義できるほか、インスタンスのメソッドを定義することもできます.
インスタンスのメソッドは、クラスで定義された関数であり、最初のパラメータは常にget_name(self)であり、メソッドを呼び出すインスタンス自体を指し、他のパラメータは通常の関数と完全に同じです.
class Person(object):

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

    def get_name(self):
        return self.__name
selfは、__init__(self, name)の最初のパラメータである例の方法である.p1.get_grade実は特殊な実例方法と見なすこともできる.
インスタンスメソッドを呼び出すには、インスタンスで呼び出す必要があります.
p1 = Person('Bob')
print p1.get_name()  # self       
# => Bob

インスタンス・メソッドの内部では、すべてのインスタンス・プロパティにアクセスできます.これにより、外部がプライベート・プロパティにアクセスする必要がある場合は、メソッド・コールによって取得できます.このデータ・パッケージの形式は、内部データの一貫性を保護するだけでなく、外部コールの難易度を簡素化することができます.
列子:Personクラスにプライベート属性を追加してください.score、スコアを表し、インスタンスメソッドget_を追加grade()、により_scoreの値はそれぞれA-優秀,B-合格,C-不合格の3段階を返す.
class Person(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def get_grade(self):
        if self.__score >= 80:
            return 'A'
        if self.__score >= 60:
            return 'B'
        return 'C'

p1 = Person('Bob', 90)
p2 = Person('Alice', 65)
p3 = Person('Tim', 48)

print p1.get_grade()
print p2.get_grade()
print p3.get_grade()

pythonのメソッドもプロパティです
classで定義したインスタンスメソッドも属性であり、実際には関数オブジェクトです.
class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        return 'A'

p1 = Person('Bob', 90)
print p1.get_grade
# => >
print p1.get_grade()
# => A

すなわち、p1.get_grade()は関数オブジェクトを返すが、この関数はインスタンスにバインドされた関数であり、types.MethodType()がメソッド呼び出しである.
メソッドもプロパティであるため、インスタンスに動的に追加することもできますが、classで関数をメソッドに変更する必要があります.
import types
def fn_get_grade(self):
    if self.score >= 80:
        return 'A'
    if self.score >= 60:
        return 'B'
    return 'C'

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

p1 = Person('Bob', 90)
p1.get_grade = types.MethodType(fn_get_grade, p1, Person)
print p1.get_grade()
# => A
p2 = Person('Alice', 65)
print p2.get_grade()
# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'
#   p2       get_grade

pythonでのクラスメソッドの定義
プロパティと同様に、メソッドにはインスタンスメソッドとクラスメソッドもあります.selfで定義されているのはすべてインスタンスメソッドであり、インスタンスメソッドの最初のパラメータ@classmethodはインスタンス自体である.
クラスメソッドをclassで定義するには、次のように書く必要があります.
class Person(object):
    count = 0
    @classmethod
    def how_many(cls):
        return cls.count
    def __init__(self, name):
        self.name = name
        Person.count = Person.count + 1

print Person.how_many()
p1 = Person('Bob')
print Person.how_many()

メソッドは、クラスのインスタンスではなくPersonクラスにバインドされます.クラスメソッドの最初のパラメータはクラス自体に渡され、通常、パラメータ名はclsと命名され、上のcls.countは実際にはPerson.countに相当する.
インスタンスではなくクラスで呼び出されるため、クラスメソッドはインスタンス変数を取得できず、クラスの参照のみを取得できます.
例:クラス属性countをプライベート属性__countに変更すると、外部から__scoreを読み取ることはできませんが、クラスメソッドで取得できます.クラスメソッドを記述して__count値を取得してください.
注意クラスメソッドには@classmethodを追加する必要があります.
    :
    class Person(object):
        __count = 0
        @classmethod
        def how_many(cls):
            return cls.__count
        def __init__(self, name):
            self.name = name
            Person.__count = Person.__count + 1

    print Person.how_many()
    p1 = Person('Bob')
    print Person.how_many()