面向对象
面向对象(OOP, Object-Oriented Programing),是一种编程方式(函数式编程和面向对象编程)中的一种,可以降低代码冗余程度。python中的类是面向对象编程的一种工具,是除函数和模块之外的另一种封装逻辑和数据的方式。
类和实例的区别:
- python中类和实例是两种不同的对象
- 类是抽象的模板,实例是根据类创建出来的一个个具体的“对象”
- 类和实例都有独立的命名空间
面向对象的三大思想
- 继承
- 多态 基于对象接口而不是类型
- 封装
1. 类的定义与实例化
python中使用class
关键字来定义一个类。
类名的命名规则:以大写字母开头
实例化一个类:实例名 = 类名(参数)
2. 类的成员
1. 属性
属性的方法方法:实例名.属性名
1.1 实例属性
由方法内部的self属性进行赋值运算产生,保存在对象(实例)中,执行只能通过对象访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class Person:
def __init__(self, name):
# 1. 在类的内部定义属性
self.name = name
tom = Person('Tom')
print(tom.name) # Tom
# 2. 在类的外部定义属性
tom.age = '18'
print(tom.age) # 18
Jerry = Person('Jerry')
print(Jerry.name) # Jerry
print(Jerry.age) # AttributeError: 'Person' object has no attribute 'age'
|
tom 和 Jerry两个对象属于两个不同的命名空间,给Tom添加的age属性在Jerry对象中并不能访问。
1.2 类属性
通过class语句内的赋值语句创建,保存在类中,执行时可以通过对象访问,也可以通过类访问
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Person:
country = 'America'
def __init__(self, name):
self.name = name
Tom = Person('Tom')
# 通过实例访问类属性
print(Tom.country) # America
# 通过类访问类属性
print(Person.country) # America
|
1.3 私有属性
在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,确保了外部代码不能随意修改对象内部的状态,但是可以间接访问。
| class Person:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
Tom = Person('Tom')
# 直接访问报错
# print(Tom.name) # AttributeError: 'Person' object has no attribute 'name'
print(Tom.get_name()) # Tom
|
2. 方法
位于类中的函数称为方法。·
类方法调用的第二种方式:class.method(instance, args,...)
,事实上,instance.method(args...)
还是会翻译成这种方式来调用
1. 构造方法
作用:
- 初始化实例属性
- 执行其他初始化任务
- 创建一个实例时自动运行
| class Bar:
def __init__(self,name,age):
self.n = name
self.a = age
print('创建对象时自动执行构造方法')
def foo(self):
print(self.n,self.a)
obj = Bar("Tom",10) # 创建对象时自动执行构造方法
obj.foo() # Tom 10
|
2. 普通方法
保存在类中,由对象调用
| class Bar:
def __init__(self):
pass
def hello(self):
print('hello world!')
obj = Bar()
obj.hello() # hello world!
|
3. 私有方法
| class Bar:
def __init__(self):
pass
def __hello(self):
print('hello world!')
def foo(self):
self.__hello()
obj = Bar()
obj.foo()
|
4. self参数
类在创建的过程中会在内存中开辟一个属于类的空间,当创建一个实例的时候也会创建属于实例的空间,当实例需要执行类中的方法,回去找类空间相应的方法,方法中的self参数,接收的就是实例本身。所以self代指调用方法的实例对象
实例时方法调用的主体,并且会自动传入给方法的self参数,把实例传入给第一个参数从而告诉方法应该处理哪一个实例。
| class Bar:
def foo(self):
print("self id:",id(self))
obj = Bar()
print("obj id:",id(obj)) # obj id: 2018867963888
obj.foo() # self id: 2018867963888
# self 和 obj 是同一个东西
|
3. 继承
一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类(基类),新类称为子类(派生类)。子类还可以自定义自己的属性和方法。
3.1 继承
| class Animal:
def run(self):
print("Animal is running...")
class Cat(Animal):
pass
Tom = Cat()
Tom.run() # Animal is running...
|
3.2 扩展
在父类的方法上添加新的功能。
| class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def pay(self, base):
print("应发工资:%s" % money)
class Manager(Person):
def pay(self, base, commission):
Person.pay(self, base+commission)
|
3.3 重写
| class Animal:
def run(self):
print("Animal is running...")
class Cat(Animal):
def run(self):
print('Cat is running...')
Tom = Cat()
Tom.run() # Cat is running...
|
3.4 组合
一个类A中,使用另一个类B的实例对象作为类A的属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Date():
'''date'''
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
class Person:
def __init__(self, name, date):
self.name = name
self.birthday = date
def show_birthday(self):
print("%s:%s-%s-%s" %(self.name, self.birthday.year, self.birthday.month, self.birthday.day))
Jerry = Person('Jerry',Date('1999','2','14'))
Jerry.show_birthday() # Jerry:1999-2-14
|
3.5 super()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class A:
def func(self):
print("This is A")
class B(A):
def func(self):
super().func()
# A.func(self) # 另一种的写法
print("This is B")
b = B()
b.func()
# 输出结果
# This is A
# This is B
|
super安装MRO顺序来寻找当前类的下一个类
python2新式类:super(子类名,self).函数名()
3.6 抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | from abc import ABCMeta, abstractclassmethod
class Super(metaclass=ABCMeta):
def __init__(self):
pass
@abstractclassmethod
def action(self):
pass
class Sub(Super):
def action(self):
print("hello")
x = Sub()
x.action() # hello
|
action函数必须在子类中定义,否则会报错
TypeError: Can't instantiate abstract class Sub with abstract methods action
3.7 多继承
- 从左到右
- 从下到上
- 广度优先
例1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | class F:
def bar(self):
print('F.bar')
class F1(F):
def boo(self):
print('F1.bar')
class F2:
def bar(self):
print('F2.bar')
class S(F1,F2): #从下往上找
pass
obj = S()
obj.bar()
#############
F.bar
|
例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class F1:
def bar(self):
print('F1.bar')
class F2:
def bar(self):
print('F2.bar')
class S(F2,F1): #从左往右找
pass
obj = S()
obj.bar()
###########
F2.bar
|
例3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | class Base:
def bar(self):
print('Base.bar')
class F(Base):
def foo(self):
print('F.bar')
class F1(F):
def foo(self):
print('F1.bar')
class F2(Base):
def bar(self):
print('F2.bar')
class S(F1,F2):
pass
obj = S()
obj.bar() #有共同的父类,最后在父类中找
############
F2.bar
|
3.7 经典类和新式类
python2.x:默认是经典类,搜索算法为DFLR(Depth First,Left to Right),深度优先。继承object
后是新式类
python3.x:没有经典类,都是新式类,隐式继承object
类,搜索算法为 MRO(method resolution order),广度优点。
4. 多态和多态性
Python3.7之多态与多态性 - 彩虹然 - 博客园 (cnblogs.com)
多态:多态是指运算的意义取决于运算的对象。(也就是说,代码不应该关心它处理的对象是什么,而应该关注这个对象做什么)。多态可以用来隐藏接口差异性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | class Animals:
def talk(self):
pass
class Cat(Animals):
def talk(self):
print("Meow...")
class Dog(Animals):
def talk(self):
print("Woof!...")
class Person(Animals):
def talk(self):
print("Ying...")
def func(animal):
# 不用关心是什么动物,只要是个动物就应该叫。
animal.talk()
xiaoming = Person()
xiaohei = Cat()
xiaohuang = Dog()
for i in [xiaoming, xiaohei, xiaohuang]:
func(i)
|
6. 装饰器
6.1 @property
把方法变成属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | class Bar:
def __init__(self):
pass
# 属性
@property
def foo(self):
return 1
@foo.setter
def foo(self,val):
print(val,"setter")
@foo.deleter
def foo(self):
print("delete")
obj = Bar()
obj.foo=123 #执行赋值语句将执行@foo.setter下对应的方法
del obj.foo #执行@foo.deleter下对应的方法
############
setter
delete
|
例1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | class Page:
def __init__(self,page):
try:
p = int(page)
except Exception as e:
p = 1
self.page = p
@property
def start(self):
val = (self.page-1) * 10
return val
@property
def end(self):
val = self.page * 10
return val
li = []
for i in range(1000):
li.append(i)
while True:
p = input("请输入页码:")
if p == "q":
break
obj = Page(p)
print(li[obj.start:obj.end])
|
例2:属性的另一种表示方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Bar:
def f1(self):
return 1
def f2(self,val):
print('f2:',val)
def f3(self):
print("f3:del")
per = property(fget=f1,fset=f2,fdel=f3,doc="注释")
obj = Bar()
print(obj.per)
obj.per = 123
del obj.per
###########
f2: 123
f3:del
|
6.2 @staticmethod
静态方法,不需要创建对象,不会隐式传递self。实例对象和类对象都可以调用。当方法和实例无相关性时使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import time
class Person:
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
@staticmethod
def show_time():
print(time.strftime("%Y-%m-%d %H:%M:%S" ))
Person.show_time() # 2020-02-15 19:17:53
Tom = Person('Tom', 18)
Tom.show_time() # 2020-02-15 19:17:53
|
6.3 @classmethod
第一个参数必须是当前类对象,该参数名一般约定为cls
,通过它来传递类的属性和方法。实例对象和类对象都可以调用。在实例化之前修改类属性的时候使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | import time
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def today(cls):
struct_time = time.localtime()
date = cls(struct_time.tm_year, struct_time.tm_mon, struct_time.tm_mday) # 自己类的实例化
return date
today = Date.today()
print(today.year,today.month,today.day) # 2020 2 15
|
7. 反射
用字符串数据类型的名字,来操作这个名字对应的函数,示例变量,绑定方法等。
反射的使用场景:
- 反射对象的实例变量、绑定方法
- 反射类中的静态变量、绑定方法、其他方法
- 反射模块中的所有变量
7.1 反射对象的实例变量、绑定方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def foo(self):
return 'hello world!'
zhangsan = Person('张三', 18)
# 1.反射对象的实例变量
print(getattr(zhangsan, 'name'))
# 2.反射对象的绑定方法
print(getattr(zhangsan, 'foo')())
# 输出结果
# 张三
# hello world!
|
7.2 反射类中的静态变量
| class Person:
country = 'China'
def __init__(self):
pass
def foo(self):
pass
print(getattr(Person, 'country'))
# 输出结果
# China
|
7.3 反射模块中的变量
1.反射模块中的变量
| import module
getattr(module,'func')
getattr(module,'Bar')
|
2.反射脚本(本文件)中的变量
| import sys
getattr(sys.modules['__main__'],func)
|
7.4 hasattr
、setattr
和delattr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Person:
country = 'China'
def __init__(self):
pass
def foo(self):
pass
Bob = Person()
# hasattr 判断是否有某个成员
print(hasattr(Bob, 'name')) # False
# setattr 设置属性
setattr(Bob, 'name', 'Bob')
print(hasattr(Bob, 'name')) # True
# delattr 删除属性
delattr(Bob, 'name')
print(hasattr(Bob, 'name')) # False
|
7.5 反射的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
while True:
inp = input("请输入要访问的URL:")
if inp == "q":
break
class Bar:
def f1(self):
return "首页"
def f2(self):
return "新闻"
def f3(self):
return "博客"
obj = Bar()
if hasattr(obj,inp):
f = getattr(obj,inp)
print(f())
else:
print("404")
|
8. 元类
9. 重装运算符
9.1__new__
是一个构造方法,用来创建一个对象需要的空间。
使用场景:创建单例模式(一个类只会创建一次self的空间)
| class Bar:
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
|
9.1 __call__方法
| class Bar:
def __init__(self):
print("init")
def __call__(self):
print("call")
obj = Bar()
obj() #对象加括号自动执行__call__方法
|
9.2 str 与 int
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Bar:
def __init__(self):
pass
def __int__(self):
return 111
def __str__(self):
return "str"
obj = Bar()
print(obj,type(obj))
r = int(obj) #int 加对象,自动执行对象的int方法,并将返回值赋值给int对象
print(r)
r = str(obj)
print(r) #str 加对象,自动执行对象的str方法,并将返回值赋值给str对象
------------
str <class '__main__.Bar'>
str
|
9.3 例:__str_
| class Bar:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return "%s:%s" %(self.name,self.age)
obj = Bar("Tom",18)
print(obj)
-----------
Tom:18
|
9.4 del 析构方法:对象销毁的时候执行
| class Bar:
def __init__(self):
pass
def __del__(self):
print("析构方法")
obj = Bar()
|
9.5 dict 将字段中的成员以字段的形式返回
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Bar:
"""注释"""
def __init__(self,name,age):
self.name = name
self.age = age
obj = Bar('Tom',18)
#将字段中的成员以字段的形式返回
print(Bar.__dict__)
print(obj.__dict__)
---------
{'__module__': '__main__', '__init__': <function Bar.__init__ at 0x0000013DAD733AE8>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': '注释', '__dict__': <attribute '__dict__' of 'Bar' objects>}
{'name': 'Tom', 'age': 18}
|
9.6 getitem 、 setitem 和 delitem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Bar:
"""注释"""
def __init__(self):
pass
def __getitem__(self,item):
return item + 10
def __setitem__(self,k,v):
print(k,v)
def __delitem__(self,val):
print('delete:',val)
obj = Bar()
print(obj[2])
obj['name']='Tom'
del obj['del']
----------------
name Tom
delete: del
|
9.7 iter
如果类中有__iter__方法,其对象就是一个可迭代对象;
对象名.iter()的返回值是迭代器
| class Bar:
def __iter__(self):
return iter([11,22,33])
obj = Bar()
for i in obj:
print(i)
#for循环执行obj对象的类中的__iter__方法,并获取其迭代器;循环上一步中返回的对象
|
10. 单例模式
应用场景:数据库连接池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Bar:
__v = None
@classmethod
def get_instance(cls):
if cls.__v:
return cls.__v
else:
cls.__v = Bar()
return cls.__v
# 不用再使用Bar()创建实例
obj1 = Bar.get_instance()
print(obj1)
obj2 = Bar.get_instance()
print(obj2)
obj3 = Bar.get_instance()
print(obj3)
###########
<__main__.Bar object at 0x0000026D865B4A20>
<__main__.Bar object at 0x0000026D865B4A20>
<__main__.Bar object at 0x0000026D865B4A20>
|
线程安全单例模式
| class Bar:
from threading import Lock
__instance = None
lock = Lock()
def __new__(cls, *args, **kwargs):
with cls.lock:
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
|