面向对象2

一.面向对象编程的三大特性

(一)继承

1.继承:子类继承父类的所有属性,若子类中有与父类属性相同的,由于作用域的影响,当调用该属性时会得到子类的该属性值。

  用一句话总结继承:给了类(子类)一个特权,能去别的类(父类)的属性字典里找其属性。

2.继承分为单继承和多继承。

class Foo:                           单继承
    name = "cwt"

class Bar(Foo):
    age = 20


print(Bar.name)>>>>cwt
class Foo:
    name = "cwt 

class Bar: 多继承 age = 20 class People(Foo,Bar): gender = "male" print(People.name) >>>>cwt print(People.age) >>>>20

3.继承的含义:

(1)继承基类的方法,且做出自己的改变或扩展(减少代码重用)。即把有相同属性的属性整合,做成一个基类。

class Animal:
    def eat:
        print("需要吃东西")
    def sleep:
        print("需要睡觉")

class Cat(Animal):
    def feature:
        print("我是猫,我会抓老鼠")
        
class Bid(Animal):
    def feature:
        print("我是鸟,我会飞")

 

(2)声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现。也称为借口继承。

例子:在python中,文件的共有属性就是都可读可写,所以定义下面的基类来声明,每个都要有读和写的功能

 

class All_file:
    def read(self):
        pass
    def write(self):
        pass

 上面的例子并不能达到声明的功能,当你没有读功能或写功能时,并不会有什么反应。所以完善如下图

class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass


class Disk(All_file):
    def read(self):
        pass

d1 = Disk()   》》》》》》报错(由于所创建的类中少了一个write功能,所以实例化时就会报错)



class Disk(All_file):
    def read(self):
        pass
    def write(self):
        pass
d1 = Disk()     》》》》》正常运行

 上面这种接口继承也称为归一化设计。这种才是继承的主要用处。

4.继承顺序:

(1.)深度优先:经典类都用该方法

(2.)广度优先:新式类都用该方法

在python3中d都是用这种方法,且可以调用  类名.__mor__的方法查看继承顺。

 

总结:在python2中父类是什么类型的,子类就是什么类型。

5。在子类中调用父类属性的方法

(1)就是在父类的属性字典里找。(这不是一种好的方法,一般别用)

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def run(self):
        pass

class Foo(People):
    def __init__(self, name, age, gender  ):
        self.name = name 
        self.age = age 
        self.gender = gender 

子类中__init__中有几个是跟父类一样的,这样就是在写重复代码。
所以可以应用在子类中调用父类的方法


class Foo(People):
    def __init__(self, name, age, gender):
        People.__init__(self,name,age)
        self.gender = gender

(2)用super来调用父类的属性。

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def run(self):
        pass

class Foo(People):
    def __init__(self, name, age, gender):
        super().__init__(name, age)
        self.gender = gender

(二).多态

1.含义:对象通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类

2.多态是一种绑定关系,只有运行时才成为多态。

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def run(self):
        pass

class L(People):
    pass

class K(People):
    pass

l1 = L("cwt",20)
k1 = K("ccc",18)

l1.run()    》》》》
k1.run()  》》》》这两步操作就称为多态(两个来

(三)封装:

1.含义:明确区分内外,内部实现逻辑,外部只能调用其方法,无法访问其内部逻辑。

2.第一种简单意义的封装:如在执行文件中调用另一个文件中定义的类(People),那么你只能调用到People中的方法,但你不知道People这个类中的逻辑,这就是简单意的封装。

3.python中的两种约定:

(1)以  _xxx   命名的属性,使用者不应该去调用(这只是一种约定,看你自己尊不遵守)

(2)以  _ _xxx  命名的属性,内部可以调用,但使用者无法调用到该属性。当是,这并不是python不让你调用,本质上是python的内在系统帮你把这个重命名了。  自动帮你改成  _类名_ _ xxx 。  

class Foo:
    def __init__(self,x):
        self.__x = x
  def  tell(self)
    print(__x)
f1
= Foo(1) print(f1.__x) 》》》》报错 print(f1._Foo__x) >>>>>1
tell() >>>>>>1 内部能调用

4.真正意义的封装:把你不想别人知道的逻辑封装起来,但后期若是很多人需要用,你需要设接口,让使用者能调用到该东西。

class Foo:
    def __init__(self,x):
        self.__x = x

    def tell(self):
        print(__x)



tell函数就是一个接口,本质上是利用在 内部能调用到

二。其它小知识点:

1.反射(自省):指程序可以访问,检测和修改它本身状态或行为的一种能力。有以attr4种方法

(1)hasattr(object, name)  >>检测object中是否含有name属性

(2)getattr(object,name,default = None)  >>访问object的属性name,有该属性就会返回其值,若没有则会报错,但当你为default赋值时,没有就会输出default的值。

(3)setattr(object,x,y)  >>为object添加属性 "x" = "y"

(4)delattr(object,x)  >>删除object中的属性x

用途:当在写一个软件时,不同功能由不同的人写的,当你 需要用到别人写的功能时,你并不知道对方代码是否都写好了,这是你就可以用反射。

文件名:test.py

class Room:
    def __init__(self,width,leight,height):
        self.width = width
        self.leight = leight
        self.height = height
    def area(self):
        room_area = self.width * self.leight 
        return room_area

 

from  test.py  import Roomdef volume(height):
    r1 = Room(20,20)
    if hasattr(Room, "area"):
        room_volume = height * r1.area()
        return room_volume
    else:
        setattr(r1,"new_area", lambda width,leight:width *leight)
        room_volume = height * r1.new_area(20,20)
        return room_volume

 .补充:

1.导入动态模块:有时,我们得到的模块路径是一个字符串,所以不能直接用from  "xxxx"  import  xxx  这样只会报错

(1)__import__("文件名")

(2)用模块来解决

模块路径:kk.t

import importlib
m = importlib.import_module("kk.t")
m.test()

2.如何把当前文件当作模块导入当前文件中:

import sys
obj = sys.modules[__name__]

 2.类的内置 attr 方法(注意:类的内置方法自己不设置时,是由默认的,当你设置后,就会按你自己设置的输出)

(1)_getattr_  ,访问的属性不存在时就会触发该函数

(2)_setattr_,设置属性时就会触发该函数,如实例化会触发__init__,__init__函数下有如 self.name = name的就会触发__serattr__

(3)_delattr_,删除属性时就会触发该函数。

这3个内置函数,与类都无关,只与实例有关:

class Foo:
    x = 1
    y = 2
    def __setattr__(self, key, value):
        print("触发setattr")
    def __getattr__(self, item):
        print("触发getattr")
    def __delattr__(self, item):
        print("触发delattr")

print(Foo.x)  不会触发__getattr__
Foo.z = 3   不会触发__setattr__
del Foo.y   不会触发__delattr__

 

class Foo:
    x = 1
    y = 2
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        print("触发setattr")
    def __getattr__(self, item):
        print("触发getattr")
    def __delattr__(self, item):
        print("触发delattr")
f1 = Foo("cwt")    》》》。触发__setattr__
f1.z = 3           》》》》触发__setattr__
del f1.y               》》》》触发_delattr__

print(f1.age)    >>>由于f1中没有该属性所以触发__getattr__

如果在类中自己写了这3个函数,而没有写东西的话,实例后,实例的属性字典是空的。简单模拟系统的默认操作

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        self.__dict__ [key] = value
    def __getattr__(self, item):
        print("没有该属性")
    def __delattr__(self, item):
        self.__dict__ .pop(item)

只能用字典的操作来进行操作,如果用点的方式来进行操作,会进入死循环。
因为用点的方法总会触发_setattr_

这几个方法可以用来对属性的格式进行设置。如不让使用者删除:_delattr_函数下什么都不写,只写个提示

    def __delattr__(self, item):
        print("不可删除")

 

3.item系列的方法

(1)_getitem_  访问属性时,无论属性是否存在,都会触发该函数

(2)_setitem_   创建属性时触发

(3)_delitem_  删除属性时触发

以上3种方法的操作都必须是用字典的操作方法才会触发

class Foo:
    def __init__(self,name):
        self.name =name
    def __getitem__(self, item):
        print("触发getitem")
    def __setitem__(self, key, value):
        print("触发setitem")
    def __delitem__(self, key):
        print("触发delitem")

f1 = Foo("cwt")   
f1["name"]            》》》触发_getitem__
f1["age"]=20       》》》触发__setitem__
f1["name"]     》》》触发__delitem__

 4.包装标准类型:可以定制自己需要的数据类型

(1)包装的一个特性,继承。

class List(list):
    def append(self, val):
        print("不可对该列表进行增值")


l1 = List("abc")    
print(l1)     》》》》["a","b","c"]

l1.append(2)  >>>>不可对该列表进行增值

上述代码,定义了一个不能添加元素的列表类,其它功能与原来一模一样。

(2)包装的另一特性:授权

import time
class Open:
    def __init__(self, file_name, mode="r", encoding="utf8"):
        self.file = open(file_name, mode=mode, encoding=encoding)
        self.mode = mode
        self.encoding = encoding
    def __getattr__(self, item):
        return getattr(self.file, item)
    def write(self,line):
        t = time.strftime("%Y:%m:%d %X")
        self.file.write("%s %s" % (t, line))


f = Open("test", "r", encoding="utf8")

f.write("hello world\n")
f.write("hello world")
print(f.read())

授权简单来说其实 就是,把本来就有的类实例化后,作为一个属性值,赋给某个属性。如上图中,把open实例化后,赋给file,这个file就有原来open这个类所有的功能。

5.isinstence(obj,cls)和issubclass(sub,super)

(1)isinstence 用来判断obl是否是类cls的实例化

(2)issubclass  用来判断sub 是否是super的子类

6.__getattribute__和__getattr__

(1)__getattribute__  无论属性是否存在都会触发该函数执行。

(2)__getattr__  当访问的属性不存在时会触发该函数。

class Foo:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self, item):
        print("getattribute")
    def __getattr__(self, item):
        print("getattr")

p1 = Foo("cwt")
p1.name   》》》》》getattribute
p1.age   >>>>>>>getattribute



可见若两个函数同时存在,则永远只会触发__getattribute__

 

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __getattribute__(self, item):
        print("getattribute")


p1.age  >>>>属性不存在的话会报错,若该操作以下还有代码得话则不会执行了

.
. 这些代码都不会执行了。所以,一般 __getattribute____getattr__都是一起用的,但同时存在不是不会执行__getattr__吗?请往下继续看
.
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __getattribute__(self, item):
        print("getattribute")
        raise AttributeError ("属性不存在")
    def __getattr__(self, item):
        print("getattr")


p1 = Foo("cwt",20)
》》》》getattribute 》》》》属性不存在
》》》》getattr

p1.age
>>>首先触发__getattribute__,由于该属性不存在,所以会执行__getattribute_函数下的raise,实行了raise后,raise会传给__getattr__一个执行的指示。所以触发__getattr__



7.改变对象的字符串显示

(1.)假如类Foo实例化后得到实例f1,print(f1)>>>会得到<__main__.Foo object at 0x02C6EDD0>     得到的这个就是对象的字符串显示。

若你不设置,系统就会以默认值输出即类似于<__main__.Foo object at 0x02C6EDD0>。

(2)两个方法

①__str__:在pycharm等的环境下使用的。

②__repr__:这个是在python的解释器中应用的,但如果不是再解释器的环境下设置该属性时,你只写了__repr__那么系统会自动以这个来代替__repr__.若两个都有,那么只会调用__str__

class Foo:
    def __str__(self):
        return "我是一个实例"
    def __repr__(self):
        return  "我是一个对象"
f1 = Foo()
print(f1)

>>>我是一个实例
class Foo:
    def __repr__(self):
        return  "我是一个对象"
f1 = Foo()
print(f1)

>>>我是一个对像

8.format()本质上是在调用类中的__format__函数属性。即自己不设置,系统会以默认形式输出。

(1)自定制格式化(format方法)

class Data:
    def __init__(self,year,mon,day):
        self.year = year
        self.mon = mon
        self.day = day
    def __format__(self, form):
        form_dict = {"ymd":"{0.year}{0.mon}{0.day}",
                "y:m:d":"{0.year}:{0.mon}:{0.day}",
        "y-m-d":"{0.year}-{0.mon}-{0.day}"}
        if  not form or form  not in form_dict:
            form = "ymd"
        f = form_dict[form]
        return  f.format(self)

d1 = Data(2019,8,15)
print(d1.__format__("y:m:d"))

9._slots_属性,该属性为一个类变量。

类的属性字典是共享的,不过实例的属性字典是独享的,即你每创建一个实例,就会生成一个实例的属性字典。

假如有一个只有一两个属性的类,不过需要实例很多次,那么就会生成很多属性字典,就会占用很多内存,这时可以用_slots_来代替属性字典

即不设__init__函数,因为这个本质上就是在创建属性字典。而改成_slots_

class Foo:
    __slots__ = "name"
    
f1=Foo()           》》》实例化后并不会产生属性字典,而是于类公用一个__slots__
f1.name = "cwt"        
print(f1.name)
print(f1.__dict__)>>>会报错,没有这个
>>>cwt

有多个属性可以用列表存放,且实例过程,只能设跟类的__slots__里有的属性
class
Foo: __slots__ = ["name","age"] f1=Foo() f1.name = "cwt" f1.age=20
f1.gender="male >>>>>报错

10.其它内置函数

(1)__del__,也称为析构方法。

作用:当对象在内存中被释放时,会自动触发该函数。(即当代码全部读完后,就会触发该函数)

class Foo:
    def __del__(self):
        print("清除完毕")
f1=Foo()
print("123")

》》》123
》》》清除完毕

(2)__doc__属性:查看类的文档字符串。

class Foo:
    "我是一个类"
    def __init__(self,x):
        self.x = x

f1=Foo(1)
print(f1.__doc__ )

》》》我是一个类

注意:该属性不能被继承,因为每定义一个新类,系统会自动将__doc__=None设置进属性字典里

class Bar:
     "我是一个类"
       pass


class Foo(Bar):   
    def __init__(self,x):
        self.x = x

f1=Foo(1)
print(f1.__doc__ )



>>>None

(3)__call__属性:对象后加个()就会触发该函数。

class Foo:
    def __init__(self,x):
        self.x = x
    def __call__(self, *args, **kwargs):
        print("我执行了")

f1 = Foo(1)
f1()

》》我执行了

 

 

11.迭代器协议

(1)迭代器协议是由类中的__iter__和__next__构成的

(2)执行next()操作时,本质上就是触发__next__

(3)解释for循环:我们知道数据类型如 list 本身并不是可迭代对象,但却能进行for循环。原理:

for  i  in   f1
①f1 变成  iter(f1)  然后触发__iter__,将 f1 变成可迭代对象
②自动触发next()进而触发__next__ 

例子

class Feibo:
    def __init__(self):
        self._a = 1
        self._b = 1

    def __iter__(self):
        return self
    def __next__(self):
# 1 1 2 3 5
        self._a, self._b = self._b, self._a + self._b

        return self._a

f1 = Feibo()
print(f1.__next__())  1
print(f1.__next__())  2
print(f1.__next__())  3
print(f1.__next__())  5 
print(f1.__next__())  8

 12.描述符

(1)含义:就是一个新式类,这个类至少要实现__get__  ,  __set__  ,   _delete__中的一个。

(2)数据描述符:实现了__set__和__get__ 的称为数据描述符,其它称为非数据描述符。

(3)基本的形式及参数

class Foo:
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass


    
class Bar:
    x = Foo()
    def __init__(self,n):
        self.x = n
        
b1 = Bar(2)  

Foo即为一个描述符
instance    =    b1
owner    =   Bar

self   =    x

 

class Foo:
    def __init__(self,key):
        self.key = key
    def __get__(self, instance, owner):
        return instance.__dict__[key]
    def __set__(self, instance, value):
        instance.__dict__ [self.key] = value
    def __delete__(self, instance):
        return instance.__dict__.pop(self.key)
class Bar:
    x = Foo("name")
    def __init__(self,n):
        self.x = n

b1 = Bar("cwt") 
print(b1.name) >>>>触发__get__
b1.age = 20   >>>>触发__set__
del b1.age    >>>>触发__delete__

(4)优先级

类属性   >    数据描述符

数据描述符   >    实例属性

实例属性     >     非数据描述符

13.类的装饰器。与函数的装饰器是一样的。

def Foo(obj) :
    obj.x=1
    obj.y=2
    return  obj


@Foo
class  Bar:
        pass
    
    

14.上下文管理协议:

(1)含义:能符合with的操作  如  with  open ("file_name")  as f

(2)方法:__enter__(self)  和__exit__(self, exc_type,exc_val,exc_tb)

class Open:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        print("进入")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("已经退出")
with Open("file") as f:                                    ①左边的 with Open   会触发__enter__并把返回值传给f
    print(123)                                              ②当with 语句下的代码都执行完了之后就会自动触发__exit__
    print(222)


>>>
进入
123
222
已经退出
class Open:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        print("进入")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("已经退出")
with Open("file") as f:
    print(123)
    print(x)                             当with以下的代码有发生错误,则会报错并执行__exit__
    print(1111)                          从错误的代码块开始,以下的都不会执行
print(33333)
>>> 进入 Traceback (most recent call last): 123 已经退出 File "E:/PycharmProjects/untitled/untitled2/study-python/pracrer.py", line 259, in <module> print(x) NameError: name 'x' is not defined
class Open:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        print("进入")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("已经退出")
        return True》》》》》》》》》》》》》》》》》》》》加上这一步,当with中的代码发生错误时,自动执行__exit__,但不会报错,所以不会影响with之外的外码  》》》》》》》》》
with Open("file") as f:
    print(123)
    print(x)
    print(1111)
print(123)


》》》
进入
123                 
已经退出
123

 

posted @ 2019-08-15 14:36  小白cwt  阅读(126)  评论(0编辑  收藏  举报