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。
通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来。