面向对象:三大特性、类成员、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

 

posted @ 2018-05-01 14:40  娄先生  阅读(768)  评论(0编辑  收藏  举报
levels of contents