Python-面向对象之基本概念,封装

1、语言分类

  三大范式:(即方法论)

    面向机器:抽象成机器指令,机器容易理解,代表:汇编

    面向过程:做一件事情,排出个步骤,第一步干什么,第二步干什么,如果出现A情况,做什么处理。

      问题规模小,可以步骤化,按部就班处理

      代表:c语言

    面向对象:OOP,随着计算机需要解决的问题的规模扩大,情况月复杂,需要很多人,很多部门协作,面向过程编程不太社和了

      代表:c++, java, Python

2、面向对象:

  面向对象:一种认识世界,分析世界的方法论,将万事抽象为类Class

  类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合

  用计算机语言来描述类,就是属性和方法的集合

  对象instance,object,对象是类的具体,是一个实体。

  哲学思想:

      一切皆对象,包括类也是对象

      对象是数据和操作的封装

      对象是独立的,但是对象之间可以互相作用

      目前OOP 是最接近人类认知的编程范式

3、面向对象的3要素:

  ① 封装

    - 组装:将数据和操作组装到一起

    - 隐藏数据:对外只暴露一些借口,通过接口访问对象

  ② 继承

    - 多复用,继承来的就不用自己写了

    - 多继承,少修改,OCP(Open-closed Principle)开闭原则,使用继承来改变,来体现个性

    - 可以多继承,Java不支持,C++ 支持

  ③ 多态

    -面向对象编程最灵活的地方,动态绑定

4、Python的类

  定义:   

1 class ClassName:
2     pass
   必须使用class关键字
  类名必须是用大驼峰命名
  类定义完成后,就产生了一个类对象,绑定到了标识符ClassName上
1 class MyClass:
2     ''' A example class'''
3     x = 'abc'
4     def foo(self):
5         return 'My Class'
6     
7 print(MyClass.x)
举例

5、类对象及类属性

  类对象:类的定义就会生成一个类对象,也就是说MyClass 就是一个对象

  类的属性:类定义中的变量和类中的方法都是类的属性

  类变量:举例中X 就是类MyClass的变量

     MyClass中x, foo 都是泪的属性,MyClass.__doc__,__doc__也是类的属性
     foo方法是类的属性,也叫方法,在类中 叫 method,普通函数 叫function,foo(self)中的self是一个惯用标识符,可以换别的,但是使用惯例,self指当前实例本身。

实例化:
1、a = MyClass() 每一次实例化,只能生产该类的一个对象
     2、使用上面的语法,在类对象名称后面加上一个括号,就调用类的实例化方法,完成实例化。
     3、实例化就真正创建一个该类的对象(实例)

4、每次实例化,产生的都是不同的对象
-----内存地址都不一样的对象
1 a = MyClass()
2 print(id(a)) # 43808192
3 a = MyClass()
4 print(id(a)) # 43808248
     5、Python类实例化后,会自动调用__init__ 方法,对实例初始化的方法:其他语言,也叫构造方法

第一个参数,必须要留给self,是可以修改名字,但是习惯用self
self:某个具体实例的本身,谁调用,就是谁!!!!!
java中用的是this

6、Python类实例化,一定调用__init__ 方法,可以定义,可以不定义,会隐式的调用父类的,自己定义,就是个性化
         作用:初始化
先实例化,在初始化:
类 --》 对象
__new__ 是实例化,现在不用管
对象 --》 给一个设定,比如颜色之类的,修饰
__init__
也就是说,实例化后,才能调用__init__ 方法

ClassName() ---- 认为实例化和初始化是接近着的,实例化,马上自动初始化!!!

7、__init__不能返回任何值,默认返回None

6、实例对象instance
 1 class Person:
 2     x = 13 # 类属性
 3 
 4     # 方法是属于类的
 5     def __init__(self, name, age):  # Python解释器送过来的,也可以手动
 6         print('init',hex(id(self)))
 7         self.name = name # 实例属性
 8         self.age = age
 9 
10     # 方法是属于类的
11     def showage(self):
12         return self.age
13 
14 print(Person)
15 print('+++++++++++++++++++++++++++++++++++++++++++++++++')
16 tom = Person('tom', 12)
17 print(tom, hex(id(tom)))
18 print(tom.name, '-------')
19 print(tom.age)
20 tom.age += 20
21 print(tom.age)
22 print(tom.showage(), '---------')
23 print('++++++++++++++++++++++++++++++++++++++++++++++++++')
24 jerry = Person('jerry', 16)
25 print(jerry, hex(id(jerry)))
26 print(jerry.name, '-----')
27 print(jerry.age)
28 jerry.age += 20
29 print(jerry.age)
30 print(jerry.showage(), '---------')
31 
32 
33 # print(tom.showage())  == print(Person.showage(tom)) 等价
  类实例化后 一定会获得一个对象,就是实例对象
  类实例化后,得到一个实例对象,实例对象 会 绑定 方法,调用 方法时采用实例.方法名 的方式。
  但是函数签名是showage(self),这个self就是jerry,Python会吧方法的调用者作为第一参数seld的实参传入。
  self.name就是jerry 对象的name,name是保存在了jerry对象上,而不是Person类上,所以,称为实例变量

  


7、实例变量和类变量:
    实例变量是每个实例自己的变量,是自己独有的:
    类变量是类的变量,是类的所有实例共享的属性和方法

            特殊属性:
__name__:对象名
类名, Person.__name__
对象是没有的,但是 tom.__class__.__name__

type() 等价 __class__
----所以 type('tom').__name__

            __class__:对象的类型


___dict__: 对象的属性的字典

__qualname__:类的限定名
      注:
对象可以调用类属性,类不能调用对象属性
       Python中每一种对象都拥有不同的属性,函数,类都是对象,类的实例也是对象。
  __dict__举例:
 1 class Person:
 2     age = 3
 3     def __init__(self, name):
 4         self.name = name
 5 
 6 
 7 print('-----class----')
 8 print(Person.__dict__)
 9 # {'__module__': '__main__', 'age': 3, '__init__': <function Person.__init__ at 0x000000000299F268>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
10 print(sorted(Person.__dict__.items()))
11 # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000000000299F268>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
12 
13 tom = Person('tom')
14 print('------istance tom ------')
15 print(tom.__class__)
16 # <class '__main__.Person'>
17 print(sorted(tom.__dict__.items()))
18 [('name', 'tom')]
19 
20 print('---tom class---')
21 print(tom.__class__.__name__)
22 # Person
23 print(sorted(tom.__class__.__dict__.items()))
24 # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000000000299F268>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
  注: 可以看到 类属性保存在类的__dict__ 中,实例属性保存在实例的__dict__ 中,如果从实例访问类的属性,就需要借助__class__找到所属的类

  举例:
 1 class Person:
 2     age = 3
 3     height = 170
 4 
 5     def __init__(self, name, age=1):
 6         self.name = name
 7         self.age = age
 8 
 9 tom = Person('tom')
10 jerry = Person('jerry', 20)
11 
12 Person.age = 30
13 print(Person.age, tom.age, jerry.age) # 30 1 20
14 
15 print(Person.height, tom.height, jerry.height) # 170 170 170
16 jerry.height = 175
17 print(Person.height, tom.height, jerry.height) # 170 170 175
18 
19 tom.height += 10
20 print(Person.height, tom.height, jerry.height) # 170 180 175
21 
22 Person.height += 15
23 print(Person.height, tom.height, jerry.height) # 185 180 175
24 
25 Person.weight = 70
26 print(Person.weight, tom.weight, jerry.weight) # 70 70 70
27 
28 print(tom.__dict__['height'])
29 print(tom.__dict__['weight']) # 报错,实例字典中没有这个key

  总结:

    是类的,也是这个类所有实例的,其 实例 都是可以访问到

    是实例的,就是这个实例自己的,通过类访问不到

    类变量是属于类的变量,这个类的所有实例可以共享这个变量

    对象(实例 或 类)可以动态的给自己增加一个属性,实例.__dict__[变量名] 和实例.变量名 都可以访问到

    实例的同名变量会隐藏掉类变量,或者说覆盖了这个类变量

 实例属性的查找顺序:
   指的是实例 使用. 点号来访问属性,会先找自己的__dict__, 如果没有,然后通过属性__class__找到自己的类,在去类的__dict__中找
   注意:如果实例 使用 __dict__[变量名]访问变量,将不会按照上面的查找顺序找变量了,这是指明使用字典的key查找,不是属性查找
      一般来说,类变量可使用全大写来命名



8、装饰一个类(区别类装饰器)
 1 # no 1
 2 def foo(cls, name='jerry'):
 3     cls.NAME=name
 4     return cls
 5 @foo
 6 class Person1:
 7     AGE = 20
 8 print(Person1.__dict__['NAME'])
 9 
10 # no 2
11 def foo(name='jerry'):
12     def wrapper(cls):
13         cls.NAME=name
14         return cls
15     return wrapper
16 
17 @foo(name='tom') # Person=foo(name='tom)(Person)=wrapper(Person)
18 class Person1:
19     AGE = 20
20 print(Person1.__dict__['NAME'])
  之所以能够装饰,本质上是为类对象动态的添加了一个属性,而Person这个标识符指向这个类对象。         

9、静态方法和类方法


    总结:
      类方法:
        1、在类定义中,使用@classmethod 装饰器修饰的方法
        2、必须至少有一个参数,且第一个参数留给了cls,cls指代调用者即类对象本身
        3、cls这个标识符可以是任意合法名称,但是不要改
        4、通过cls可以直接操作类的属性
          注意:无法通过cls操作类的实例,有类不一定有实例
      静态方法:
        1、在类定义中,使用@staticmethod装饰器修饰的方法
        2、调用时,不会隐式的传参
        3、只是表明这个方法属于这个名词空间,函数跪在一起,方便组织管理


10、方法的调用
  

  
  总结:
    类几乎可以调用所有内部定义的方法,但是调用普通方法 时会报错,原因是第一参数必须是类的实例
    实例也几乎可以调用所有的方法,普通函数的调用一般不可能出现,因为不允许这么定义
    
    类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数
    实例可以调用所有类中定义的放到(包括类方法,静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类



11、访问控制:
  1、私有属性(Private)
 1 # no1 原本是想通过控制方法,控制age,
 2 class Person:
 3     def __init__(self, name, age):
 4         self.name = name
 5         self.age = age
 6 
 7     def growup(self, i=1):
 8         if i>0 and i<100:
 9             self.age += 1
10 
11 p1 = Person('tom',12)
12 p1.growup(20)
13 p1.age = 160 # 但是超界了,还是会修改
14 print(p1.age)
15 
16 # no2  __age 作为私有,双下划线
17 class Person:
18     def __init__(self, name, age):
19         self.name = name
20         self.__age = age
21 
22     def growup(self, i=1):
23         if i>0 and i<100:
24             self.__age += 1
25 
26 p1 = Person('tom',12)
27 p1.growup(20)
28 # print(p1.__age) # 获取不到,根本没有这样的一个属性,报错
29 
30 
31 
32 class Person:
33     def __init__(self, name, age):
34         self.name = name
35         self.__age = age
36 
37     def growup(self, i=1):
38         if i>0 and i<100:
39             self.__age += 1
40 
41     def getage(self):
42         return self.__age
43 
44 p1 = Person('tom',12)
45 p1.__age = 1000 # 这个是自己另外给p1实例增加了一个__age 属性,并不是私有的属性
46 print(p1.__age) # 1000
47 print(p1.getage()) # 12
48 
49 
50 print(p1.__dict__)
51 # {'name': 'tom', '_Person__age': 12, '__age': 1000}
52 # 看字典就明白了
53 # 事实上,私有的是修改了名字, _Person__age
54 # 知道了这个名字 可以直接修改了
55 p1._Person__age = 10
56 print(p1.getage()) # 10
  2、保护变量:
 1 class Person:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self._age = age
 5 
 6     def growup(self, i=1):
 7         if i>0 and i<100:
 8             self.age += 1
 9 
10 tom = Person('tom',12)
11 print(tom._age) # 12
12 print(tom.__dict__) # {'name': 'tom', '_age': 12}

      可以看出,这个_age 属性根本就有没改变名称,和普通的属性一样,解释器不做任何特殊处理,这只是开发者共同的约定,不要直接使用

12、私有方法:类似私有属性

 1 class Person:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5 
 6     def _getname(self):
 7         return self.name
 8 
 9     def __getage(self):
10         return self.age
11 
12 
13 tom = Person('tom',12)
14 # print(tom.__getage()) #报错,没有
15 print(tom._getname())
16 print(tom.__dict__)
17 print(tom.__class__.__dict__)
18 print(tom._Person__getage())
19 
20 # tom
21 # {'name': 'tom', 'age': 12}
22 # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000000000297C8C8>, '_getname': <function Person._getname at 0x00000000029402F0>, '_Person__getage': <function Person.__getage at 0x0000000002992840>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
23 # 12

13、补丁:

  可以通过修改或者替换类的成员,使用者调用的方式没有改变,但是,类提供的功能可能已经改变了

  猴子补丁(Monkey Patch)

  在运行时,对属性,方法,函数等进行动态替换

  其目的往往是为了通过替换,修改来增强,扩展原有代码的能力

  慎用!!!!!!

    之前:

    之后:

14、属性装饰器:

  一般好的设计是:把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性

 1 # 普通方式
 2 class Person:
 3     def __init__(self, name, age=18):
 4         self.name = name
 5         self.__age = age
 6 
 7     def age(self):
 8         return self.__age
 9 
10     def set_age(self, age):
11         self.__age = age
12 
13 tom = Person('tom')
14 print(tom.age()) # 18
15 tom.set_age(20)
16 print(tom.age()) # 20
17 
18 # 装饰器方式
19 class Person:
20     def __init__(self, name, age=18):
21         self.name = name
22         self.__age = age
23 
24     @property
25     def age(self):
26         return self.__age
27 
28     @age.setter
29     def age(self, age):
30         self.__age = age
31 
32     @age.deleter
33     def age(self):
34         print('del')
35 
36 tom = Person('tom')
37 print(tom.age) # 18
38 tom.age = 30
39 print(tom.age) # 30

  特别注意:使用property装饰器的时候这三个方法同名

    其他写法:

    还可以如下:使用了匿名函数,但是set没办法,因为匿名函数条件不能出现等号

15、对象销毁

  类中可以定义__del__ 方法,称为析构方法(函数)

  作用:销毁类的实例的时候调用,以释放占用的资源,其中就放些清理资源的代码,比如释放链接,关闭文件句柄

  注意这个方法不能疫情对象的真正的销毁,只是对象销毁的时候会自动调用它.

  使用del 语句删除实例,引用计数减1, 当引用计数为0 时,会自动调用__del__方法。

  由于Python实现了垃圾回收机制,不能确定对象何时执行垃圾回收

  注:由于垃圾回收 对象销毁时,才会真正清理对象,还会在回收对象之前自动调用__del__ 方法,除非你明确自己的目的,建议不要手动嗲用这个方法

16、方法重载(overload)

  在Java,等语言中,会有重载的概念

  所谓重载 ,就是同一个方法名,但是参数数量,类型不一样,就是同一个方法的重载。

  Python没有重载,也不需要重载,*args可以解决多参,且强类型,动态语言,不需要指定参数类型

17、封装

  面向对象的三要素之一,封装Encapsulation

  封装:

    将数据和操作组织到类中,即属性和方法

    将数据隐藏起来,给使用者提供操作(方法), 使用者通过操作就可以获取或者修改数据,getter和setter。

    通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来。

为什么要坚持,想一想当初!
原文地址:https://www.cnblogs.com/JerryZao/p/9643857.html