• python之面向对象


    一、对象的特性

    对象的特性:

    1、实例属性 

    定义,分两种情况:

    • 类内定义:必须在__init__()方法中创建并初始化
    • 类外定义:self.<属性名> = 值,可在外部创建并初始化,不建议这样做。

    使用方法:

    • 类内使用 self.<属性名>
    • 类外使用 实例名.<属性名>

    特性:

    • 类被实例化以后才会有的属性
    • 相同类的不同实例属性不相干

      

    2、类属性

    定义:在类内定义,无需实例化即可使用

    作用:类属性使得相同类的不同实例共同持有相同变量

    调用方法:

    • 类名调用方式:类名.类属性
    • 实例调用方法:实例名.类属性

    3、私有属性

    __ 开始的变量是私有成员(实例属性),意思是只有类对象自己能访问,实例对象、子类对象不能访问到这个数据。

    #encoding=utf-8
    class A():
        def __init__(self):
            self.__a = 10
    
        def info(self):
            print self.__a
    
    class B(A):
        pass
    
    if __name__ == '__main__':
        a = A()
        # print a.__a       #实例对象不能访问这个数据,dir(a)中表明变量名已变为_A__a
        a.info()            #打印信息:10  说明:类对象自己可以访问
        print dir(a)        #打印信息:['_A__a', '__doc__', '__init__', '__module__', 'info']
        a.__a = 100
        a.info()            #打印信息:10
        print a.__a         #打印信息:100
        print dir(a)        #打印信息:['_A__a', '__a', '__doc__', '__init__', '__module__', 'info']
    
        b = B()
        b.info()            #打印信息:10   说明:子类继承父类所有的公有实例变量和方法,只有通过方法才可调用其私有实例变量
        print dir(b)        #打印信息:['_A__a', '__doc__', '__init__', '__module__', 'info']
        b.__a = 200
        b.info()            #打印信息:10
        print b.__a         #打印信息:200
        print dir(b)        #打印信息:['_A__a', '__a', '__doc__', '__init__', '__module__', 'info']

    说明:

    Python把以两个或以上下划线字符开头且没有以两个或以上下划线结尾的变量当作私有变量,因为私有变量会在代码生成之前被转换为长格式(变为公有)。转换机制是这样的:在变量前端插入类名,再在前端加入一个下划线字符。这就是所谓的私有变量轧压(Private name mangling)

    也就是self.__a = 0被转换为self._A__a = 0

    如果运行代码改为这样:

    if __name__ == '__main__':
        a = A()
        a.info()
    
        a._A__a = 100
        a.info()
    

    运行结果:

    0
    100
    

    但不建议这样做,既已声明为私有变量,就不要在类外访问。 

    __ 开始的变量是私有成员(类属性),同样只有类对象自己能访问,实例对象、子类对象不能访问到这个数据;强制访问也不行。

    class A():
        __tmp = 'gjp'
        def __init__(self):
            self.__a = 0
    
        def info(self):
            print(self.__a)
            print(A.__tmp)
    
    if __name__ == '__main__':
        print(A.__tmp)
        print(_A__A.__tmp)
    

    运行报错!!!

      

    _ 开始的变量成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量,但还是通过类提供的接口进行访问;

    作为全局变量时,不能用'from module import *'导入

    class A():
        def __init__(self):
            self._a = 0
    
        def info(self):
            print(self._a)
    
    class B(A):
        pass
    
    
    if __name__ == '__main__':
        a = A()
        a.info()                #result:0  说明:实例a具有实例变量 + 方法,方法调用其自身实例变量,肯定是OK的
        a._a = 100
        a.info()                #result:100  说明:a的实例变量已被重新赋值
        print(a._a)             #result:100  说明:a的实例变量可直接调用
    
        b = B()
        b.info()                #result:0  说明:子类B继承A的实例变量 + 方法
        b._a = 200b
        b.info()                #result:200  说明:b的实例变量已被重新赋值
        print(b._a)             #result: 200  说明:b的实例变量可直接调用
    

      

    _ 开头变量类属性

    class A():
        tmp1 = 'www'
        _tmp2 = 'hester'
    
    if __name__ == '__main__':
        print(A.tmp1)
        print(A._tmp2)
    

    运行结果:

    www
    hester

    __开头和结尾的(__foo__)代表python里特殊方法专用的标识,如 __init__()代表类的构造函数,不建议使用。 

    4、特殊属性

    作用:保存对象的元数据

    • __doc__ 用于保存文档字符串
    • __name__ 保存类的名称
    • __dict__ 保存实例属性名
    • __module__ 保存所在模块名
    • __base__ 保存类的父类

    二、对象具有能动性

    三、深入类的属性

    同名的类属性与实例属性调用优先级:

    以实例名.属性名引用时,优先引用实例属性;以类名.属性名引用时,只能引用类属性。 

    class A():
        a = 0
        def __init__(self):
            self.a = 100
            self.b = 200
    
    if __name__ == '__main__':
        a = A()
        print(a.a)
        print(A.a)
        print(a.b)
    

    运行结果:

    100
    0
    200
    

    在给一个稍微特殊的例子

    class A():
        count = 10
        pass
    
    if __name__ == '__main__':
        a = A()
        print a.count       #result is : 10
        a.count = 100
        print a.count       #result is : 100
        print A.count       #result is : 10

    属性访问的特殊方法(反射),提供用字符串来操作类的属性/方法

    • hasattr(obj_bame,’属性名‘)
    • setattr(obj_name,'属性名',值)
    • getattr(obj_name,’属性名‘)
    class A():
        a = 0
        def __init__(self):
            self.a = 100
            self.b = 200
    
    if __name__ == '__main__':
        a = A()
        print(getattr(a,'b'))
        setattr(a,'b',2000)
        print(getattr(a,'b'))
        print(hasattr(a,'cc'))
        print(hasattr(a,'a'))
    

    运行结果:

    200
    2000
    False
    True
    

    属性包装

      

    class A(object):
        def __init__(self):
            self._weight = 100
            self.width = 200
    
        @property
        def weight(self):
            return self._weight
    
        @weight.setter
        def weight(self, value):
            if 0 < value <= 500:
                self._weight = value
            else:
                print('Set Failture!')
        
    if __name__ == '__main__':
        a = A()
        print(a.weight)
        a.weight = 1000
        print(a.weight)
    

    运行结果:

    100
    Set Failture!
    100
    

    注:类必须继承自object类,包装的变量必须为私有变量_x

    看到这里,有些同学可能会觉得使用set、get函数操作变量也可以达到相同的效果,代码如下:

    class Studentscore(object):
        def __init__(self,score):
            self._score = score
    
        def set_score(self,score):
            self._score = score
    
        def get_score(self):
            return self._score
    
    
    if __name__ == '__main__':
        s = Studentscore(30)
        print s.get_score()
        s._score = 60
        print s.get_score()
    

    但如果想将score设置为只读属性,如上代码就无能无力了。property意义便显现出来,代码如下:

    class Studentscore(object):
        def __init__(self,score):
            self._score = score
    
        @property
        def score(self):
            return self._score
    
    if __name__ == '__main__':
        s = Studentscore(30)
        print s.score
        # s.score = 90      # AttributeError: can't set attribute
    

    外部已无法直接设置score,运行报错。

    说句题外话,既然_score已设为私有属性,请大家不要在外部直接使用s._score = xx,这样有违初衷;也说明python的私有属性设置非强制性  

    property不仅有可读属性,还有可写、删除属性,完整代码如下:

    class Studentscore(object):
        def __init__(self,score):
            self._score = score
    
        @property
        def score(self):
            return self._score
        @score.setter
        def score(self,score):
            if not isinstance(score,int):
                raise ValueError('score must be an integer')
            if score < 0 or score > 100:
                raise ValueError('score must between 0 ~ 100')
            self._score = score
    
        @score.deleter
        def score(self):
            del self._score
    
    if __name__ == '__main__':
        s = Studentscore(30)
        print s.score
        s.score = 90
        print s.score
        # s.score = 120     ValueError('score must between 0 ~ 100')
    

    虚拟属性的例子:

    class Studentscore(object):
        def __init__(self,score):
            self._score = score
            self._pass = 60
    
        @property
        def score(self):
            return self._score
    
        @property
        def over_score(self):
            return self._score - self._pass
    
    if __name__ == '__main__':
        s = Studentscore(90)
        print s.score
        print s.over_score
    

      运行结果:

    90
    30
    

      

      

    class NoNeg(object):
        def __init__(self,default = 0):
            self.default = default
    
        def __set__(self,instance,value):
            if value > 0:
                self.default = value
            else:
                print("The Value must be nonnegative")
    
        def __get__(self,instance,owner):
            return self.default
    
        def __delete__(self):
            pass
    
    class Movie(object):
        rating = NoNeg()
        score = NoNeg()
    
    if __name__ == '__main__':
        m = Movie()
        print('rating:',m.rating)
        print('score:',m.score)
        m.rating = -4
        m.score = 90
        print('rating:',m.rating)
        print('score:',m.score)   
    

    运行结果:

    ('rating:', 0)
    ('score:', 0)
    The Value must be nonnegative
    ('rating:', 0)
    ('score:', 90)
    

    注:两个类必须为新式类  

      

    所有的类成员函数都是非数据描述符:

    class Test():
        def pr(self):
            print('Test')
    
    if __name__ == '__main__':
        t = Test()
        print(dir(t.pr))
    

    运行结果:

    ['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'im_class', 'im_func', 'im_self']
    

    pr函数实现了__get__方法

    __call__让类的实例如函数一样可调用:

    class Test():
        def __call__(self):
            print('Test calling')
    
    if __name__ == '__main__':
        t = Test()
        t()
    

    运行结果:

    Test calling
    

      

    同名的实例属性与非数据描述符的访问优先级:

    class A(object):
        def pr(self):
            print('calling')
    
    if __name__ == '__main__':
        a = A()
        print(a.pr)
        a.pr = 10
        print(a.pr)
        del a.pr
        print(a.pr)
    

    运行结果:

    <bound method A.pr of <__main__.A object at 0x022025F0>>
    10
    <bound method A.pr of <__main__.A object at 0x022025F0>>
    

    说明:同名的实例属性会隐藏(或者说覆盖掉)同名非数据型描述符  

      

    四、类方法与静态方法

    静态方法:  

    class Washer(object):
        def __init__(self,water = 100,scour = 2):
            self._water = water
            self.scour = scour
    
        @staticmethod
        def spoons_ml(spoon):
            return spoon * 0.4
    
        @property
        def water(self):
            return self._water
    
        @water.setter
        def water(self,water):
            if 0 < water <= 500:
                self._water = water
            else:
                print("enter water failture")
    
        def add_water(self):
            print('add water:',self._water)
    
        def add_scour(self):
            print("add scour:",self.scour)
    
        def start_wash(self):
            self.add_water()
            self.add_scour()
            print("start wash...")
    
    if __name__ == '__main__':
        #call using class
        print Washer.spoons_ml(8)
        #call using instance
        w1 = Washer()
        print w1.spoons_ml(8)
        #using staticmethod to init instance
        w2 = Washer(300,Washer.spoons_ml(9))
        w2.start_wash()
    

    运行结果:

    3.2
    3.2
    ('add water:', 300)
    ('add scour:', 3.6)
    start wash... 

    类方法:

    class Washer(object):
        def __init__(self,water = 100,scour = 2):
            self._water = water
            self.scour = scour
    
        @staticmethod
        def spoons_ml(spoons):
            return spoons * 0.4
    
        @classmethod
        def get_washer(cls,water,spoons):
            return cls(water,cls.spoons_ml(spoons))
    
        @property
        def water(self):
            return self._water
    
        @water.setter
        def water(self,water):
            if 0 < water <= 500:
                self._water = water
            else:
                print("enter water failture")
    
        def add_water(self):
            print('add water:',self._water)
    
        def add_scour(self):
            print("add scour:",self.scour)
    
        def start_wash(self):
            self.add_water()
            self.add_scour()
            print("start wash...")
    
    if __name__ == '__main__':
        w = Washer.get_washer(60,8)
        w.start_wash()
    

    运行结果:

    ('add water:', 60)
    ('add scour:', 3.2)
    start wash...
    

    classmethod中三个cls,也可以改为类名;但使用cls更加有优势,更改类名之后,无需更改内部代码。  

    四、类的继承与方法重载  

    继承类调用方法、属性顺序,先在继承类中寻找方法、属性;如果没有,向父类中继续寻找;仍然没有,向祖父类中寻找...

    多重继承时方法的调用按照深度优先的顺序进行调用

    class A(object):
        def foo(self):
            print("A foo...")
    
    class B(object):
        def foo(self):
            print("B foo...")
    
    class C(A,B):
        pass
    
    class D(B,A):
        pass
    
    if __name__ == '__main__':
        c = C()
        c.foo()
    
        d = D()
        d.foo()
    

    运行结果:

    A foo...
    B foo...
    

    五、类的特殊方法  

    class A(object):
        pass
    
    #bind a variable
    tmp = A
    a = tmp()
    print a
    
    #add attribute to class
    a.sub = 100
    print a.sub
    
    #as a function parameter
    def sum(cls):
        return cls()
    
    print sum(tmp)
    

    运行结果:

    <__main__.A object at 0x016526B0>
    100
    <__main__.A object at 0x01AF2510>
    

      

    构造一个序列类 

    需要实现如下部分方法:

    1、__len__(self)

    2、__getitem__(self,key)
    3、__setitem__(self,key,value)

    4、__delitem__(self,key)

    class MySeq:
        def __init__(self):
            self.lseq = ["I","II","III","IV"]
    
        def __len__(self):
            return len(self.lseq)
    
        def __getitem__(self,key):
            if 0 <= key <4:
                return self.lseq[key]
    
    if __name__ == '__main__':
        m = MySeq()
        for i in range(4):
            print(m[i])
    

    运行结果:

    I
    II
    III
    IV
    

    自定义一个迭代器  

    需要实现如下方法:

    1、__iter__(self)

    2、__next__(self)

    class MyIter:
        def __init__(self,start,end):
            self.count = start
            self.end = end
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.count < self.end:
                r = self.count
                self.count += 1
                return r
            else:
                raise StopIteration
    
    if __name__ == '__main__':
        for i in MyIter(1,10):
            print(i)  

    运行结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    自定义可比较类  

    需要实现如下部分方法:

    • __gt__() 大于
    • __ge__() 大于、等于
    • __lt__() 小于
    • __le__() 小于、等于
    • __eq__() 等于
    • __nq__() 不等于
    class Point:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __lt__(self,oth):
            return self.x < oth.x
    
        def __gt__(self,oth):
            return self.y > oth.y
    
    if __name__ == '__main__':
        pa = Point(0,1)
        pb = Point(1,0)
        print(pa < pb)
        print(pa > pb)
    

    运行结果:

    True
    True
    

    自定义可逻辑运行类  

    需要实现如下部分方法:

    • __add__()
    • __sub__()
    • __mul__()
    • __div__()
    class Point:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __add__(self,oth):
            return Point(self.x + oth.x,self.y + oth.y)
    
        def init(self):
            print(self.x,self.y)
    
    if __name__ == '__main__':
        pa = Point(2,3)
        pb = Point(3,4)
        pc = pa + pb
        pc.init()
    

    运行结果:

    5 7
    

    六、多态  

    通过继承实现多态(子类可以作为父类使用),也就是作为同一父类的子类,当父类可作为函数参数时,子类也可以。

    class Animal(object):
        def move(self):
            print("Animal moving...")
    
    class Dog(Animal):
        pass
    
    def move(obj):
        obj.move()
    
    a = Animal()
    move(a)
    
    d = Dog()
    move(d)
    

    运行结果:

    Animal moving...
    Animal moving...

    子类通过重载父类的方法实现多态,也就是子类对同一动作可以有不同反应。

    class Animal(object):
        def move(self):
            print("Animal moving...")
    
    class Cat(Animal):
        def move(self):
            print("Cat moving...")
    
    def move(obj):
        obj.move()
    
    a = Animal()
    move(a)
    
    c = Cat()
    move(c)
    

    运行结果:

    Animal moving...
    Cat moving...
    

    python是一种动态语言,变量绑定的类型具有不确定性

    a = 1
    a = 'abc'
    a = 1.2
    

    所有函数跟方法可以接受不同类型的参数

    def info(obj):
        print(type(obj))
    
    info(3)
    info(1.3)
    info('hester')
    

    调用是否成功取决于参数方法和属性,调用不成功则抛出错误。

    def info(obj):
        obj.move()
    
    class Cat():
        def move(self):
            print('Cat moving...')
    
    info(Cat())
    # info('hester')  AttributeError: 'hester' object has no attribute 'move'
    

    所以python中不需要接口  

    七、类的设计模式  

    单实例  

    class myclass:
        def __new__(cls,*args,**kwargs):
            if not hasattr(cls,'_name'):
                cls._name = super().__new__(cls,*args,**kwargs)
            return cls._name
    
    if __name__ == '__main__':
        mc1 = myclass()
        mc2 = myclass()
        print(id(mc1))
        print(id(mc2))
    

    运行结果:

    7331568
    7331568
    

    实现工厂方法

    class Ab:
        a = 1
    
    class Ac:
        b = 2
    
    class MyFactory:
        def get_instance(self,ins):
            return ins()
    
    if __name__ == '__main__':
        mf = MyFactory()
        print(id(mf.get_instance(Ab)))
        print(id(mf.get_instance(Ac)))
    

    运行结果:

    18276136
    18276304
    

    八、实现策略模式  

    定义:让对象的某个方法可以随时改变,而不用更改其内部代码

    • 将类作为参数进行传递  

    class A:
        def move(self):
            print('move...')
    
    class B(A):
        def move(self):
            print('move on B')
    
    class C(A):
        def move(self):
            print('move on C')
    
    class Moveobj:
        def set_move(self,A):
            self.a = A()
    
        def move(self):
            self.a.move()
    
    if __name__ == '__main__':
        m = Moveobj()
        m.set_move(A)
        m.move()
    
        m.set_move(B)
        m.move()
    
        m.set_move(C)
        m.move()
    

    运行结果:

    move...
    move on B
    move on C
    

    实例通过调用set_move方法,可以实现不同的行为;实际使用中,其他的类可以不用继承同样的父类,只要保证有相同的方法即可。  

    • 将函数作为参数进行传递

    def movea():
        print('move on a')
    
    def moveb():
        print('move on b')
    
    class Myclass:
        def set_move(self,obj):
            self.obj = obj
    
        def move(self):
            self.obj()
    
    if __name__ == '__main__':
        m = Myclass()
        m.set_move(movea)
        m.move()
        m.set_move(moveb)
        m.move()
    

    运行结果:

    move on a
    move on b
    

    九、类的装饰模式  

    作用:通过继承可以获得父类的属性,通过重载可以修改其方法;而装饰模式可以不以继承的方式修改其方法,可以不以继承的方式返回一个被修改的类。

    核心:装饰器中定义和被装饰类相同名的方法

    class Demo:
        def be_edit_method(self):
            print('be edit...')
    
        def be_keep_method(self):
            print('be keep...')
    
    class Decoroter:
        def __init__(self,ins):
            self._ins = ins()
    
        def be_edit_method(self):
            print('start edit...')
            self._ins.be_edit_method()
    
        def be_keep_method(self):
            self._ins.be_keep_method()
    
    if __name__ == '__main__':
        de = Demo()
        de.be_edit_method()
        de.be_keep_method()
    
        dc = Decoroter(Demo)
        dc.be_edit_method()
        dc.be_keep_method()
    

    运行结果:

    be edit...
    be keep...
    start edit...
    be edit...
    be keep...
    

    装饰器的另一种实现

    class Water:
        def __init__(self):
            self.name = 'water'
    
        def show(self):
            print(self.name)
    
    class Deco:
        def show(self):
            print(self.name)
    
    class Sugar(Deco):
        def __init__(self,water):
            self.name = 'Sugar'
            self.water = water
    
        def show(self):
            print(self.name)
            print(self.water.name)
    
    class Salt(Deco):
        def __init__(self,water):
            self.name = 'Salt'
            self.water = water
    
        def show(self):
            print(self.name)
            print(self.water.name)
    
    if __name__ == '__main__':
        w = Water()
        # w.show()
        s = Sugar(w)
        s.show()
    
        s = Salt(w)
        s.show()
    

    运行结果:

    Sugar
    water
    Salt
    water
    

    实际中,python已有现成的类装饰器,可以直接使用,代码如下:

    def deco(a_class):
        class NewClass:
            def __init__(self,age,color):
                self.wrapped = a_class(age)
                self.color = color
    
            def display(self):
                print(self.color)
                print(self.wrapped.age)
    
        return NewClass
    
    @deco
    class Cat:
        def __init__(self,age):
            self.age = age
    
        def display(self):
            print(self.age)
    
    if __name__ == '__main__':
        c = Cat(4,'black')
        c.display()
    

    运行结果:

    black
    4
    

    说明:在函数中自定义类,对传入的类进行修饰,然后返回自定义类  

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

  • 相关阅读:
    JCL: What is EXCP
    百分比布局的使用
    使用TabLayout快速实现一个导航栏
    彻底理解android中的内部存储与外部存储
    Eclipse的LogCat总是自动清空怎么办?
    怎么给Unity写一个原生的插件
    一句话、一张图记住Activity和Fragment之间错综复杂的生命周期关系
    2015年工作中遇到的问题:131-140(有图才有真相)
    2015年工作中遇到的问题:131-140(有图才有真相)
    雷观(二十四):谈谈我对国家事务“二胎”和“教育”的一些看法
  • 原文地址:https://www.cnblogs.com/hester/p/5091967.html
Copyright © 2020-2023  润新知