zoukankan      html  css  js  c++  java
  • 面向对象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
    ")
    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
  • 相关阅读:
    深入理解javascript原型和闭包(3)——prototype原型
    深入理解javascript原型和闭包(2)——函数和对象的关系
    深入理解javascript原型和闭包(1)——一切都是对象
    js 基本类型与引用类型的区别
    一次完整的HTTP事务是怎样一个过程
    PHP+MySql+jQuery实现的“顶”和“踩”投票功能
    PHP获得真实客户端的真实时用到的IP REMOTE_ADDR,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR
    Jenkins设置自动发邮件
    Jenkins+SVN+maven+Tomcat构建自动化集成任务
    Jenkins详细安装教程
  • 原文地址:https://www.cnblogs.com/chenweitao/p/11351410.html
Copyright © 2011-2022 走看看