Python学习笔记之面向对象高级编程

一、继承

       在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类,二被继承的class称为基类,父类或者超类。

#!/usr/local/python3/bin/python3
# -*- conding:utf-8 -*-
# Animals是父类/基类;
class Animals(object):
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight
    def eat(self):
        print ("%s eating......" % (self.name))
        self.weight += 2
    def drink(self):
        print( "%s is drinking....." % (self.name))
        self.weight += 1
    def get_weight(self):
        pass
        # Dog是Animal的子类/派生类;
class Dog(Animals):
        # 类里面的方法第一个参数必须是self
    def jiao(self):
        print ("%s wang wang ......." % (self.name))
        # Cat是Animal的子类/派生类;
class Cat(Animals):
    def jiao(self):
        print( "%s miao miao miao........" % (self.name))
tom = Dog("tom", 12, 10)
tom.eat()
tom.jiao()

输出:

tom eating......
tom wang wang .......

二、子类需要新的属性时

       当我们一个雷继承里一个类的时候,这个类就有了他继承的那个类的所有的非私有的属性与方法,当我们的子类想要添加新的属性的时候,就必须重写父类的构造函数

# Dog是Animal的子类/派生类;
class Dog(Animals): # name, age, weight, dogid
    def __init__(self, name, age, weight, dogid):
        # 第一种重写父类构造方法;
        #self.name = name
        #self.age = age
        #self.weight = weight
        # 第二种重写父类构造方法:
        # 让Dog的父类执行它的__init__方法;
        #Animals.__init__(self, name, age, weight)
        # 第三种重写父类构造函数的方法;
        super(Dog, self).__init__(name, age, weight)
        self.dogid = dogid
super( 自己类的名称 , self).__init__( 形参 )
        不需要明确告诉父类的名称;
        如果父类改变,只需修改
class 语句后面的继承关系即可 

三、python2中的经典类,python3中的新式类     

#python3 中的新式类:
class 类名(父类):
    pass

@python2 中的经典类

class 类名:
    pass
新式类方法与属性继承时 子类使用广度优先算法,经典类方法与属性继承时,子类使用深度优先算法


新式类的广度优先算法验证

class D(object):
    def test(self):
        print("D test")
class C(D):
    def test(self):
        print("C test")
class B(D):
    def test(self):
        print("B test")
class A(B,C):
    def test(self):
        print("A test")

a = A()
a.test()

当我们一次将A B C 的test()方法注释掉时,我们多次运行的结果为

  A test -> B test -> C test -> D test

经典类的深度优先算法验证

class D:
    def test(self):
        print("D test")
class C(D):
    pass
    def test(self):
        print("C test")
class B(D):
    pass
    # def test(self):
    #     print("B test")
class A(B,C):
    pass
    # def test(self):
    #     print("A test")

a = A()
a.test()

当我们一次将A B ,D 的test()方法注释掉时,我们多次运行的结果为

A test -> B test -> D test -> C test

 四、特殊的类属性

   1、class.__name__ 、class.__doc__ 、 class.__dict__ 方法
class Base(object):
    pass
class Animals(object):
    """
    父类Animals:
    Attritube:
    name:
    age:
    weight:
    """
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight
    def eat(self):
        print ("%s eating......" % (self.name))
        self.weight += 2
class Cat(Animals):
    def eat(self):
        print( "%s eating......" % (self.name))
        self.weight += 1
class Dog(Animals, Base):
    def eat(self):
        print ("%s eating......" % (self.name))
        self.weight += 3
print(Animals.__name__)
print(Animals.__doc__)
# # 打印类的所有父类,以元组类型返回;
print (Animals.__bases__)
print (Dog.__bases__)
# 以字典的方式返回类的方法和属性;
print (Dog.__dict__)

# 如果类不是被导入的, 显示为__main__;
# 如果类是被import导入的, 则显示类所在的模块名
print(Animals.__module__)
 2、class.__str__ 、class.__del__ 方法

        当我们在类中添加下面方法时:

 
 
    def __str__(self):
        pass
    def __del__(self):
        pass
       当我们打印类的对象时,解释器会自动调用__str__ 方法,当我们需要删除这个对象的时候我们有会调用__del__方法(类的析构函数,与 __init__ 类的构造函数相反)
class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score
        print("student is create")

    def __str__(self):
        return "Student:<%s,%s,%s>" %(self.name, self.age, self.score)
    def __del__(self):
        print("student is delete")
s1 = Student("westos", 19, 100)
print(s1)
 3、class.__iter__ 方法

       如果一个类想要被for...in循环,必须使用类的内置方法 def __iter__(self)  ,该方法返回一个类的迭代对象。当我们的类在外部调用时,需要被使用迭代环境时候,def __iter__(self) 方法会自动被调用,返回一个可迭代的对象,被外部调用使用。

 4、类的运算符重载
method overload call
__init__ 构造函数 对象创建: X = Class(args)
__del__ 析构函数 X对象收回
__add__ 云算法+ 如果没有_iadd_, X+Y, X+=Y
__or__ 运算符| 如果没有_ior_,X|Y, X|=Y
_repr__, __str__ 打印,转换 print(X),repr(X),str(X)
__call__ 函数调用 X(*args, **kwargs)
__getattr__ 点号运算 X.undefined
__setattr__ 属性赋值语句 X.any=value
__delattr__ 属性删除 del X.any
__getattribute__ 属性获取 X.any
__getitem__ 索引运算 X[key],X[i:j]
__setitem__ 索引赋值语句 X[key],X[i:j]=sequence
__delitem__ 索引和分片删除 del X[key],del X[i:j]
__len__ 长度 len(X),如果没有__bool__,真值测试
__bool__ 布尔测试 bool(X)
__lt__, __gt__, 
__le__, __ge__, 
__eq__, __ne__
特定的比较 X<Y,X>Y,X<=Y,X>=Y, 
X==Y,X!=Y 
注释:(lt: less than, gt: greater than, 
  le: less equal, ge: greater equal, 
  eq: equal, ne: not equal 
__radd__ 右侧加法 other+X
__iadd__ 实地(增强的)加法 X+=Y(or else __add__)
__iter__, __next__ 迭代环境 I=iter(X), next()
__contains__ 成员关系测试 item in X(任何可迭代)
__index__ 整数值 hex(X), bin(X),  oct(X)
__enter__, __exit__ 环境管理器 with obj as var:
__get__, __set__, 
__delete__
描述符属性 X.attr, X.attr=value, del X.attr
__new__ 创建 在__init__之前创建对象
from collections import Iterable
class Student(object):
    def __init__(self, name, score=(100, 90, 100)):
        self.name = name
        self.scores = list(score)

    def __iter__(self):
        # 迭代学生对象时,默认迭代的属性
        return iter(self.scores)

    def __gt__(self, other):  # 运算符 > 号重载
        print('运算符 > 重载被调用')
        avg_score1 = sum(self.scores) / len(self.scores)
        avg_score2 = sum(other.scores) / len(other.scores)
        return avg_score1 > avg_score2

    def __lt__(self, other):
        print('运算符 < 重载被调用')
        avg_score1 = sum(self.scores) / len(self.scores)
        avg_score2 = sum(other.scores) / len(other.scores)
        return avg_score1 < avg_score2

    def __le__(self, other):
        print('运算符 <= 重载被调用')
        avg_score1 = sum(self.scores) / len(self.scores)
        avg_score2 = sum(other.scores) / len(other.scores)
        return avg_score1 <= avg_score2

    def __ge__(self, other):
        print('运算符 >= 重载被调用')

        avg_score1 = sum(self.scores) / len(self.scores)

        avg_score2 = sum(other.scores) / len(other.scores)
        return avg_score1 >= avg_score2

    def __eq__(self, other):
        print('运算符 == 重载被调用')
        avg_score1 = sum(self.scores) / len(self.scores)
        avg_score2 = sum(other.scores) / len(other.scores)
        return avg_score1 == avg_score2

s1 = Student('LI', [100, 109, 90])
s2 = Student('AD', [123, 464, 0])

print(isinstance(s1, Iterable))
print(s1 > s2)
print(s1 < s2)
print(s1 == s2)
print(s1 >= s2)
print(s1 <= s2)

输出:

True
运算符 > 重载被调用
False
运算符 < 重载被调用
True
False
运算符 >= 重载被调用
False
运算符 <= 重载被调用
True
       我们通过使用def  __iter__方法使类的对象变为了一个可迭代的对象,但是我们还是不能使用索引,或者像字典一样使用关键字进行检索
    def __getitem__(self, index):
        #当对象名[key] 为右值时会自动调用__getitem__方法
        return self.scores[index]

    def __setitem__(self, index, value):
        #当对象名[key]当左值时被调用
        self.scores[index] = value

    def __delitem__(self, index):
        #del 对象名[key] 时 调用
        del self.__dict__[index]

       我们将上的类的方法加入,就可以使用索引了,但是不能使用key-value进行检索。

s1 = Student('LI',[100,109,90])
s2 = Student('AD',[123,464,0])
print(s1[0])
print(s1[1])
print(s1[2])
s1[1]=900
print(s1[1])

输出:

100
109
90
900

       我们使用了下标作为类的索引,就不能使用key-value值进行检索了,如果需要,我们就在__getitem__方法与__setitem__的最后返回一个__dict__[key]对象,类的__dict__方法会将我们类的属性和属性对应的值变为字典封装在一起。

    def __getitem__(self, key):
        #当对象名[key] 为右值时会自动调用__getitem__方法
        return self.__dict__[key]

    def __setitem__(self, key, value):
        #当对象名[key]当左值时被调用
        self.__dict__[key] = value

    def __delitem__(self, key):
        #del 对象名[key] 时 调用
        del self.__dict__[key]

输出:

{'name': 'LI', 'scores': [100, 109, 90]}
{'name': 'AD', 'scores': [123, 464, 0]}
LI
90
 5、property 

       在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是没办法检查参数,导致该对象的这个属性被任意修改,因此我们使用了私有属性,但是当我们想要取者属性时,就比较麻烦了,还要写一个内置的方法,调用也不大方便,

       Python中有一个被称为属性函数(property)的小概念, 能做以下几点:
            1). 属性方法的作用就是通过@property把一个方法变成一个静态属性 
            2). 重新实现一个属性的setter和getter方法

class Book(object):
    def __init__(self, name, author, state, bookIndex):
        self.name = name
        self.author = author
        # 0:借出 1:未借出
        self.__state = state
        self.bookIndex = bookIndex
    # 打印对象时自动调用;str(对象)
    def __str__(self):
        # return "书名:{0.name} 状态:{0.state}".format(self)
        return "书名:{book.name} 状态:{book.state}".format(book=self)
    @property
    #把类的方法变为属性
    #调用类的方法 : book1.state()
    #调用类的属性 : book1.state
    def state(self):            #当book1.state 当右值时调用
        if self.__state == 0:
            return '借出'
        else:
            return '未借出'

    @state.setter   
    def state(self,value):    #当book1.state 当左值时调用
        if value in [0,1]:
            self.__state = value
            print('状态更改成功')
        else:
            raise TypeError('状态只能是0或者1')

    @state.deleter            #当book1.state 有关键字del 时调用
    def state(self):
        print('成功删除')
        del self.__state
book1 = Book('Python','guido',1,'INDEX313')
print(book1.state)
book1.state = 0
print(book1.state)
del book1.state

输出:

未借出
状态更改成功
借出
成功删除

 6、绑定属性与方法

       当我们定义一个类,并对该类进行了实例化,此时我们也可以对该实例化对象绑定任何属性与方法,这就是动态语言的灵活性,有如下一个类:

class Student(object):
    pass

     我们可以发现该类并没有任何属性与方法,我们此时对该类添加一个class.name属性与class.set_name(self,name)方法:

        在动态绑定中分为两种绑定方式,一种是实例绑定,另一种是类绑定。我们先看实例绑定

    s = Student()
    s.name = 'xiao ai'
    print(s.name)

        输出:

    'xiao ai'

        绑定一个方法:

    for types import MethodType
    def set_name(self,name):
        self.name = name
    s.set_name = MethodType(set_name,s)
    s.set_name('na mei')
    print(s.name)

        输出:

    na mei

        在实例绑定中还有一种方法可以绑定一个方法:

    def set_name(self,name):
        self.name = name
    s.set_name = set_name
    s.set_name(s,'na mei')
    print(s.name)

        输出:

    na mei

        以上是一种动态绑定的方式,这种绑定方式只能够对当前实例使用,还有一种绑定是对类进行的,这种绑定方式,对实例化后的所有对象都可以使用。

        对Student类绑定一个age属性:

    >>> class Student(object):
    ...     pass
    >>> Student.age = 18
    >>> s1 = Student()
    >>> s2 = Student()
    >>> print(s1.age , s2.age)
    18 18

        对Student类绑定一个set_age(age) 方法

    >>> def set_age(self,age):
    ...     self.age = age
    ...     
    >>> Student.set_age = set_age
    >>> s1.set_age(19)
    >>> s1.age
    19
    >>> s2.set_age(25)
    >>> s2.age
    25
        我们使用MethodType的模块,当我们使用MethodType将set方法绑定到Student类中,并不是将这个方法直接写到Student类内部,而是在内存中创建一个link指向外部的方法,在创建Student实例的时候Link也会被复制,所以不论创建多少实例,这些实例和Student类都指向同一个Set_age()方法,同样,s1.set_age(19),也并没有在s1这个实例的内部去创建age这个属性,而是将x属性创建在外部set方法的内存区中。
        
>>> class Student(object):
...     pass
... 
>>> Student.age = 18
>>> def set_age(self,age):
...     self.age = age
...     
>>> from types import MethodType
>>> Student.set_age=MethodType(set_age,Student)
>>> s1 = Student()
>>> s2 = Student()
>>> s1.set_age(100) , s2.set_age(150)
(None, None)
>>> s1.age,s2.age
(150, 150)
     因为所有实例享用的是同一个引用的方法,所以整个内存只存在一个。因为s2.set_age(150)比s1.set_age(100)后调用,所以后面的会覆盖掉前面的,所以所有实例该方法的值都是150

        但是,如果我们想要限制class的属性怎么办,比如,只允许Student实例添加name和age属性,甚至不允许添加任何属性怎么办?

        为了达到这一限制目的,Python允许class在定义的时候,定义一个特殊的__slots__变量,来限制该class可以添加的属性。

>>> class Student(object):
...     __slots__ = ('name', 'age')
...     
>>> s1 = Student()
>>> s1.age
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: age
>>> s1.age = 15    #成功
>>> s1.age
15
>>> s1.name = 'da xue'
>>> s1.name
'da xue'
    
s.score = 99 # 绑定属性'score'   失败
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
       由于'score'没有被放到__slots__中,所以不能绑定 score 属性,试图绑定 score 将得到AttributeError 的错误

       使用__slots__要注意,__slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的: 

 7、类的反射
       有时候我们会碰到这样的需求,需要执行对象的某个方法,或是需要对对象的某个字段赋值,而方法名或是字段名在编码代码时并不能确定,需要通过参数传递字符串的形式输入。这个机制被称为反射或是自省(反过来让对象告诉我们他是什么)
1. 访问对象的属性
#
dir([obj]):
hasattr(obj, attr):
getattr(obj, attr):
setattr(obj, attr, val):
2. 确定对象的类型
isinstance(object, classinfo):
# hasattr: 判断类的对象是否拥有该属性或方法;
print(hasattr(open1, 'name'))
print(hasattr(open1, 'mode'))
print(hasattr(open1, 'get_name'))
print(hasattr(open1, 'set_name'))
print(hasattr(open1, '__eq__'))    #父类继承的方法和属性也为真
# getattr()获取对象的属性的值,如果指定返回值,对象不存在会报错;
print(getattr(open1, 'name'))

#
if hasattr(open1, 'name1'):
    print(getattr(open1, 'name1'))
else:
    print("no name1 property")
# 获取对象‘name1’的属性值,如果‘name1’属相不存在,则返回'Error'
print(getattr(open1, 'name1', 'Error'))
# 获取对象的方法的地址,如果该方法存在则返回该方法的内存地址,(该内存地址是一个函数体)
fun = getattr(open1, 'get_name')  #变量fun指向了该方法所在的内存地址,成为该方法的另一个别名
print(fun)
print(fun())    #使用fun对该方法进行调用
#setattr设置对象属性的值,该属性存在返回该属性的值,如果不存在,
#则新建对该对象添加该属性,并将该属性的值设置为要修改的值。;
setattr(open1, 'name', '/etc/group')
print("open1.name: ",  getattr(open1, 'name'))
setattr(open1, 'ad1111', '/etc/group')
print("open1.name: ",  getattr(open1, 'ad1111'))
print(open1.ad1111)
#此时‘__1111’不是私有属性外部可以访问
setattr(open1, '__ads1111', '/etc/group')
print("open1.name: ",  getattr(open1, '__ads1111'))
print(open1.__ads1111)
print(dir(open1))

 8、类的with ... as ... 语句的实现

       我们在打开一根文件时可以使用with语句打开,这样保证了我们在一段代码结束之后我们的文件是关闭的,同样我们也可以对类去实现with语句,这样我们就可以在with语句结束时自动类的实例化对象申请的内存进行释放等工作,保证了内存的安全。

       

class MyOpen(object):
    def __init__(self,filename,mode = 'r'):
        self.filename = filename
        self.mode = mode
    def __del__(self):
        pass
    
    def read(self):
        return self.file_it.read()
    
    def __enter__(self):
        self.file_it = open(self.filename,'r')
        return self.file_it 
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file_it.close()
 
        

        如上面所示我们通过 def __enter__(self) 和 def __exit__(self, exc_type, exc_val, exc_tb) 魔术方法实现了类对with语句的支持。

        def __enter__(self)语句会在下面语句执行时自动被调用。

        with MyOpen(pwd) as file_it

        其本质的过程是执行了下面两个语句。

    file_it = MyOpen(pwd)      open(file_it.filename)   

       def __exit__(self, exc_type, exc_val, exc_tb) 语句会在with语句块执行完毕的时候自动调用,执行。我们在这里保证了,我们打开的每一个文件都会在结束之后关闭,并且,file_it这个对象也会被析构,这一段时间申请的内存也会被释放。






猜你喜欢

转载自blog.csdn.net/m0_37717595/article/details/80405323