zoukankan      html  css  js  c++  java
  • 面向对象:三大特性、类成员、property

    一、类的基础知识

      python 一切皆为对象。

      我们以前的str,list,int 都是对象。

    1.1 类的定义 与 调用

      class 关键字用来定义类,注意类名首字母大写。

      类的调用,先实例化一个类,叫做对象或实例。使用这个实例调用其中的方法。

      其实在之前我们一直在使用面向对象,str本身就是一个类。 

         s = ‘abc’  -》 s = str(abc)  # 看源码对= 进行了运算符的重载,生成一个对象。就是我们类的调用。

         s.upper()   调用里面的方法。

    class Foo:
        def run(self):
            print('run')
    obj = Foo()
    obj.run()

    1.2类 与 实例的存储 self 的含义

      在定义类的方法的时候,都需要写一个self,但是在调用的时候又不要写。

      其实self就是调用者本身。把self传递给类。self永远是调用者本身。这在继承的时候非常关键。

      在python运行的时候,

          类存储着本身的各种方法与一些类属性

          实例只存储自身的属性,与类的内存指针

      当两个实例去调用类里面的方法的时候,使用self传递本身,在调用类中同一个方法,这样在调用的时候就能区别,是谁调用了这个方法。

    class Foo:
        def run(self, arg):
            print(self, arg)
    
    
    obj1 = Foo()
    obj1.run(111)
    print(obj1)
    self
    class F:
    
        def f1(self):
            print('F.f1')
    
        def f2(self):
            print('F.f2')
    
    
    class S(F):
        def s1(self):
            print('S.s1')
    
    obj = S()
    
    obj.s1()
    obj.f1()   # S中没有f1,F中有,obj去执行,上面的self还是obj
    self 永远是调用者本身

    二 、面向对象三大特征

      面向对象的三大特性:

        1. 封装

        2.继承

        3.多态

    2.1 封装

      封装是把属性关联到实例(对象)中。下次调用的时候,直接去实例中调用。

      python封装分为两种(本质是一样的):

          使用构造函数__init__进行封装

          动态属性封装

    2.1.1 构造方法 __init__

      类名+()  就会自动执行__init__

      在创建对象(实例化)的时候,类内部会自动调用__init__方法,别名构造方法。利用这个特性我们就可以把属性封装到里面。

      封装和构造方法是没有关系的,我们是利用构造方法的特性来封装。

      这样所有的实例,都有相同的属性,同时可以通过对象去调用。

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

    obj = Foo()
    obj.name

    2.1.2 动态属性

      创建对象之后,动态的添加,但是这种添加只属于该实例本身。

    class Foo:
        def run(self, arg):
            print(self, arg)
    obj1 = Foo()
    obj1.a = 1
    obj1.b = 'abc'
    print(obj1.a)
    print(obj1.b)

      新加的属性,只是保存在这个实例的内存中,其他的实例的内存没有。

      但是这种方法,会造成实例属性的不一致,造成管理的难度。一般都是构造函数统一。

      在类中有个特殊方法__slots__,可以对动态属性进行了限制。

    class Foo:
        __slots__ = ('name', 'age', 'sex')  # 实例只能有这些属性
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    obj = Foo('li', 18, 'M')
    obj.v = 'v'                  # 报错,不能添加__slots__以外的动态属性

    2.1.3 从封装角度去选择函数 or 面向对象

      如果多个函数中有一些相同参数时,就可以把函数转换为面向对象。

      如下例子,如果使用函数,ip,port,username,pwd等参数在每个函数中都要去写。

           如果转换为面向对象,那么把这些参数封装起来,下面的方法都可以 使用了。

        class DataBaseHelper:
        
            def __init__(self, ip, port, username, pwd):
                self.ip = ip
                self.port = port
                self.username = username
                self.pwd = pwd
            
            def add(self,content):
                # 利用self中封装的用户名、密码等   链接数据
                print('content')
                # 关闭数据链接
            
            def delete(self,content):
                # 利用self中封装的用户名、密码等   链接数据
                print('content')
                # 关闭数据链接
            
            def update(self,content):
                # 利用self中封装的用户名、密码等   链接数据
                print('content')
                # 关闭数据链接
                
            def get(self,content):
                # 利用self中封装的用户名、密码等   链接数据
                print('content')
                # 关闭数据链接

    2.2 继承

      继承就像老子传递给儿子一些东西。 比如金钱,一些习惯。但是一些坏习惯,还有媳妇是不能继承的。

      继承遵循开放和封闭原则(修改禁止,支持扩展)。如果一些web框架或其他模块,我们直接下载下来进行修改,部署到生产线上,我们要对所有的web框架的源代码进行修改。

      这样很麻烦,而且出现问题的时候,不知道是哪出错了,这时候我们就要使用继承来扩展功能了。

    2.2.1 继承的表现形式

    class F:
    
        def f1(self):
            print('F.f1')
    
        def f2(self):
            print('F.f2')
    
    class S(F):
        def s1(self):
            print('S.s1')
    
    obj = S()
    
    obj.s1()
    obj.f1()   # s中没有 f1方法,但是因为继承了,所以也能执行

    2.2.2 重写 

      重写类似与,我不想用父类的方法,想自己写这个方法。只要同名的就可以了。

    class F:
        def f1(self):
            print('F.f1')
        def f2(self):
            print('F.f2')
    class S(F):
        def s1(self):
            print('S.s1')   
        def f2(self):      # 默认S如果没有重写f2,会执行父类的f2.现在重写了,执行自己的f2.
            print('S.f2')
    obj = S()
    obj.s1()
    obj.f1()

    2.2.3  执行父类的方法。

       执行父类的方法。当我们重写了某个方法,但是又想在这个重写的函数中,执行下父类的该方法。

      (一般都这这么使用的,当然也可以执行父类的其他方法。因为默认都继承了父类的方法。只有重写了才需要执行父类的方法)

      那我们就可以使用super。  

    class F:
        def f1(self):
            print('F.f1')
        def f2(self):
            print('F.f2')
    
    class S(F):
        def s1(self):
            print('S.s1')   
        def f2(self):
            print('S.f2')
            super(S, self).f2()  # 执行父类的方法,也可以执行,父类的其他方法
            # F.f2(self)         # 也可以主动执行,这种方法必须在函数运行时,必须主动传递self。 不推荐这种使用方法。
    obj = S()
    obj.f2()
    
    运行结果:
    S.f2
    F.f2

    2.2.4 多继承

      在java和php其他语言中是不支持多继承的。python和c++支持。

      多继承查找规则

        1.左侧的优先,一条路走到底,如果左侧没有,则在右边的执行

        2.如果有共同的跟,跟是查找所有之后,最后一步执行的

    如上图,如果一个方法只存在Base和F2中,S执行该方法,优先查找F2。如下代码

    class base:
        def a(self):
            print('base.a')
    
    class F0(base):
        def a1(self):
            print('F0.a')
    
    
    class F1(F0):
        def a1(self):
            print('F1.a')
    
    
    class F2(base):
        def a(self):
            print('F2.a')
    
    
    class S(F1, F2):
        pass
    
    s = S()
    s.a()
    交叉继承实例

    多继承一个案列解析(类似socketserver源码):

      self永远是执行者本身,调用方法的时候,要从self的类中开始查找该方法。

    class BaseRequest():
        def __init__(self):
            print('BaseRequest.INit')
    
    class RequestHandler(BaseRequest):
    
        def __init__(self):
            print('RequestHandler.init')
            super(RequestHandler,self).__init__()
    
        def serve_forever(self):
            print('RequestHandler.server_forver')
            self.process_request()
        
        def process_request(self):
            print('RequestHandler.process_request')
    
    class Minix:
    
        def process_request(self):
            print('Minix.process_request')
    
    class Son(Minix, RequestHandler):
        pass
    
    obj = Son()
    obj.serve_forever()   # ,进入到requestHandler中的serve_forver,但是该方法中有个process_request
                          #self是Son对象,所以又会重新开始查找,根据查找次序,左侧优先,Minix中有。
    多继承类似socket源码揭破

    2.3 多态

      python中不用考虑多态。python原生就是多态的。

      java中申明一个变量必须强指定一个类型,函数接受的时候必须严格指定接受参数的类型。

      python中一个变量的类型是根据内容进行转换的,在函数接受参数的时候也可以随意。

    三、类成员

      一个类中有属性和方法,统称为成员。下面我们对类成员进行了简单的分类。

    # 属性
      - 普通属性,保存在对象中,执行只能通过对象访问          对象实例化时候创建,每个对象有自己一份属性
      - 静态属性,保存在类中,   执行可以通过对象访问 也可以通过类访问      类初始化时候创建,所有人共用一份,节约内存

    # 方法
      - 普通方法,保存在类中,由对象来调用,self=》对象
      - 静态方法,保存在类中,由类直接调用
      -    类方法,保存在类中,由类直接调用,cls=》当前类

    class Province:
        # 静态属性
        country = '中国'
    
        def __init__(self, name):
            # 普通属性
            self.name = name
    # 普通方法 def bar(self): print(self.name)
    # 静态方法 @staticmethod def sta(): print('1')
    # 类方法 @classmethod def stac(cls): print('123')

    3.1 属性

      python中的属性就只有两种类型:普通方法 和 静态方法。

    3.1.1 普通属性

      在我们利用__init__或动态添加实例的属性,这些都是普通属性,在对象实例化的时候产生,每个对象都有自己一份属性,相互之间不干扰。

    3.1.2 静态属性

      静态属与类,保存在类中,对象和类都可以访问,在类初始化的时候创建。只保存一份。

      当一个对象修改了静态属性,其他看到的都是修改了(后面我们可以把其定义为私有的,避免修改)。

      应用场景:所有对象,都有共同的属性,如果用普通属性,每个对象中都会保存一份,大大的消耗内存,这样就可以使用静态属性了。

          比如国籍,学校的地址等

    3.2 方法

      python的方法我们分为三类:普通方法,静态方法,类方法。

    3.2.1 普通方法

      上面我们定义的都是普通方法,普通方法有两种调用形式:

    方法1:
    obj = Foo()
    obj.run()        # 有个self,自动把obj传递过去
    方法2: 
    obj = Foo()
    Foo.run(obj)   # 手动传递对象,一般都不使用这种方式    

    3.2.2 静态方法

    定义形式:  

    class Foo:
        @staticmethod
        def sta():
            print('1')

    不需要传递对象self,就相当于一个普通方法。由类来调用,但是也可以用对象来调用,一般都不这么做。

    在一些功能,不依赖于对象中的参数的时候,就可以这么来定义。类似函数,可以自己定义函数参数,不需要self传递东西

    3.2.3 类方法

      类方法定义:

    class Foo:
        @classmethod   
        def stac(cls):    # 需要传递一个类
            print('123')

    四、 property

      property是一个比较有意思的东西。它是把一个方法 像 属性一样去调用。分页的场景中可以使用。(属性具有 查看,删除,设置方法。)

      property其实 就是指定的代码语法 映射到类中指定的方法去接受。  类方法中,想执行什么都是我们自己定义的。

      有两种实现方式:

    第一种:使用装饰器的方法实现,方法名需要一致。

    class Foo:
        @property    # 映射取值
        def per(self):
            print('我是不伦不类的', '123')
        
        @per.setter     # 映射设值
        def per(self, val):
            print(val)
        
        @per.deleter    # 映射删除
        def per(self):
            print('我执行了删除')
    
    obj.per                     # 取值
    obj.per = '这是啥东西?'      # 设值
    del obj.per                 #

    第二种实现:直接使用内置函数property。代码实现方式:在一些源代码中会这么写

    class Foo:
        def f1(self):
            return 123
        
        def f2(self, v):
            print(v)
        
        def f3(self):
            print('映射了删除')
    
        per = property(fget=f1, fset=f2, fdel=f3)
    
    obj = Foo()
    obj.per
    obj.per = '这是啥东西?'
    del obj.per
  • 相关阅读:
    Javascript代码收集
    JS表自动取值赋值
    数据分析04 /基于pandas的DateFrame进行股票分析、双均线策略制定
    数据分析03 /基于pandas的数据清洗、级联、合并
    数据分析02 /pandas基础
    数据分析01 /numpy模块
    爬虫07 /scrapy图片爬取、中间件、selenium在scrapy中的应用、CrawlSpider、分布式、增量式
    爬虫06 /scrapy框架
    爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取
    爬虫04 /asyncio、selenium规避检测、动作链、无头浏览器
  • 原文地址:https://www.cnblogs.com/louhui/p/8976256.html
Copyright © 2011-2022 走看看