一、定义:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
1.创建类和对象:面向对象编程是一种编程方式,需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
class 类名:
静态属性1 = '静态属性1' # 公开静态属性 只有是这个类的就有这个属性
__静态私有属性2 = '__静态属性2' # 私有静态属性 只有是这个类的就有这个属性
def __init__(self): # 初始化方法 self是对象,是一个必须传的参数
self.name = 'felix' #公开对象属性
self.__age = 18 #私有对象属性
def func1(self): #类的动态公开方法 一般情况下必须传self参数
pass
def __func2(self): #类的动态私有方法 一般情况下必须传self参数
pass
对象 = 类名() #类的实例化 创建对象
对象.静态属性1 #引用类的静态属性1
对象.func1()
说明:
- 关键字:class 使用关键字class 来创建一个类
- 关键字:self 一般情况,必须传的一个参数,代表类本身
- 创建对象:类名称后加括号即可
- 类的公开属性和方法可以在外部调用
- 类的私有属性和方法不能在类的外部调用,只能在类的内部调用
import math class Circle: def __init__(self,r): self.r = r def area(self): return math.pi*(self.r**2) def perimeter(self): return 2*math.pi*self.r A = Circle(5) print(A.area()) print(A.perimeter())
2.静态属性 就是直接在类中定义的变量
(1)类中的静态变量,可以被对象调用
(2)对于不可变数据类型来说,类变量最好用类操作
(3)对于可变数据类型来说,对象名的修改时共享的,重新赋值是独立的
3.动态属性 就是类中定义的方法(函数)
4.命名空间:类和对象分别存在不同的命名空间中
4.组合:一个对象的属性值是另外一个类的对象
import math
class Circle:
def __init__(self,r):
self.r = r
def area(self):
return math.pi*(self.r**2)
def perimeter(self):
return 2*math.pi*self.r
class Ring:
def __init__(self,outside_r,inside_r):
self.outside_r = Circle(outside_r)
self.inside_r = Circle(inside_r)
def area(self):
return self.outside_r.area() - self.inside_r.area()
def perimeter(self):
return self.outside_r.perimeter() + self.inside_r.perimeter()
R = Ring(20,10)
print(R.area())
print(R.perimeter())
三、三大特性之封装
封装就是在变量的左边加上双下划线,有以下三种情况:
1.类中的静态私有属性
2.类中的私有方法
3.对象的私有属性
所有的私有属性和方法都不能在类的外部调用。
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性
def __get_pwd(self): # 私有方法
return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名
def login(self): # 正常的方法调用私有的方法
self.__get_pwd()
felix = Person('felix','felix666')
print(felix._Person__passwd) # _类名__属性名
print(felix.get_pwd()) #报错,类外部不能调用私有属性
执行结果:
felix666
AttributeError: 'Person' object has no attribute 'get_pwd'
四、三大特性之继承
1.定义
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
2.单继承和多继承
class ParentClass1: #定义父类(基类、超类)
pass
class ParentClass2: #定义父类(基类、超类)
pass
class SubClass1(ParentClass1): #Subclass是子类(派生类) 单继承 ,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass
实例:
class Animal: #父类 基类 超类
def __init__(self):
print('执行Animal.__init__')
self.func()
def eat(self):
print('%s eating'%self.name)
def drink(self):
print('%s drinking'%self.name)
def func(self):
print('Animal.func')
class Dog(Animal): #子类 派生类
def guard(self): #派生方法
print('guarding')
def func(self):
print('Dog.func')
dog = Dog()
- 父类中没有的属性,在子类中出现 叫做派生属性
- 父类中没有的方法,在子类中出现 叫做派生方法
- 只要是子类的对象调用,子类中有名字的,一定用子类的;子类中没有才找父类的,如果父类也没有就报错
- 如果父类、子类都有的属性和方法,就使用子类的;如果还想用父类的(单独调用父类的), 调用方法:父类名.方法名(需要自己传self参数)
调用方法:父类名.方法名(self) #需要传自己的self参数
super().方法名 #不需要自己传self python3
3.继承顺序
那么问题又来了,多继承呢?
- 是否可以继承多个类
- 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
class F:
def func(self):
print('F')
class D(F):
def func(self):
super().func()
print('D')
class E(F):
def func(self):
super().func()
print('E')
class B(D):
def func(self):
super().func()
print('B')
class C(E):
def func(self):
super().func()
print('C')
class A(B,C):
def func(self):
super().func()
print('A')
a = A()
a.func()
print(A.mro())
执行结果:
F
E
C
D
B
A
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]
新式类和经典类
关于多继承寻找父类的顺序:新式类广度优先 经典类深度优先
新式类中 有一个类名.mro方法 查看广度优先的继承顺序
在Python3中,有一个super方法,根据广度优先的继承顺序查找上一个类
- Python的类可以继承多个类,Java和C#中则只能继承一个类
- Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先(py3)
4.接口类和抽象类
- 接口类和抽象类都是面向对象的规范,所有的接口类和抽象类都不能实例化
- 接口类是支持多继承的,抽象类不支持多继承
(1)接口类
接口类的单继承:
class Wechat(Payment):
def pay(self,money):
print('已经用微信支付了%s元'%money)
class Alipay(Payment):
def pay(self,money):
print('已经用支付宝支付了%s元' % money)
class Applepay(Payment):
def pay(self,money):
print('已经用applepay支付了%s元' % money)
def pay(pay_obj,money): # 统一支付入口
pay_obj.pay(money)
# wechat = Wechat()
# ali = Alipay()
app = Applepay()
# wechat.pay(100)
# ali.pay(200)
p = Payment()
接口类的多继承:
# Tiger 走路 游泳
# Swan 走路 游泳 飞
# Eagle 走路 飞
from abc import abstractmethod,ABCMeta
class Swim_Animal(metaclass=ABCMeta):
@abstractmethod
def swim(self):pass
class Walk_Animal(metaclass=ABCMeta):
@abstractmethod
def walk(self):pass
class Fly_Animal(metaclass=ABCMeta):
@abstractmethod
def fly(self):pass
class Tiger(Walk_Animal,Swim_Animal):
def walk(self):
pass
def swim(self):
pass
class Eagle(Fly_Animal,Walk_Animal):
def fly(self):
pass
def walk(self):
pass
class Swan(Swim_Animal,Walk_Animal,Fly_Animal):
def walk(self):
pass
def swim(self):
pass
def fly(self):
pass
t = Tiger()
e = Eagle()
s = Swan()
(2)抽象类
import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
all_type='file'
@abc.abstractmethod #定义抽象方法,无需实现功能
def read(self):
'子类必须定义读功能'
with open('filaname') as f:
pass
@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
'子类必须定义写功能'
pass
class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')
class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的读取方法')
class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('进程数据的读取方法')
def write(self):
print('进程数据的读取方法')
wenbenwenjian=Txt()
yingpanwenjian=Sata()
jinchengwenjian=Process()
#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()
print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
小结:
# java :
java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题
# python
python中没有接口类概念,但是直接用多继承,实现了接口类 abc模块中的meraclass=ABCMeta @abstructmethod
python中支持抽象类 : 一般情况下 单继承 不能实例化
Python 支持单继承也支持多继承 所以对于接口类和抽象类的区别就那么明显了
五、三大特性之多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
优点 : 松耦合 每个相似的类之间都没有影响
缺点 : 太随意了,只能靠自觉
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
多态性:
peo=People()
dog=Dog()
pig=Pig()
#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()
#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
obj.talk()
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
def read(self):
pass
def write(self):
pass
class DiskFile:
def read(self):
pass
def write(self):
pass
六、类的装饰器
类的内置函数
1.@property 作用是在新式类中返回属性值。
实例1:
import math
class Circle:
def __init__(self,r):
self.r = r
@property
def perimeter(self): #用了类的装饰器 property 后,不能在此处加参数
return 2 * math.pi * self.r
@property
def area(self): #用了类的装饰器 property 后,不能在此处加参数
return math.pi * (self.r ** 2)
c1 = Circle(5)
print(c1.area) #用了 类的property装饰器后 area 变成了 c1的属性了
print(c1.perimeter) #用了 类的property装饰器后 perimeter 变成了 c1的属性了
实例2:
class Person:
def __init__(self,name):
self.__name = name
@property
def name(self):
return self.__name + '牛牛牛'
@name.setter
def name(self,new_name):
self.__name = new_name
felix = Person('felix')
print(felix.name)
felix.name = 'all'
print(felix.name)
2.@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
实例1:
class A(object):
bar = 1
def func1(self):
print ('foo')
@classmethod
def func2(cls):
print ('func2')
print (cls.bar)
cls().func1() # 调用 foo 方法
A.func2() # 不需要实例化
输出结果为:
func2
1
foo
实例2:
class A(object):
# 属性默认为类属性(可以给直接被类本身调用)
num = "类属性"
# 实例化方法(必须实例化类之后才能被调用)
def func1(self): # self : 表示实例化类后的地址id
print("func1")
print(self)
# 类方法(不需要实例化类就可以被类本身调用)
@classmethod
def func2(cls): # cls : 表示没用被实例化的类本身
print("func2")
print(cls)
print(cls.num)
cls().func1()
# 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
def func3():
print("func3")
print(A.num) # 属性是可以直接用类本身调用的
# A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
A.func2()
A.func3()
实例3:
class Goods:
__discount = 0.8
def __init__(self,name,price):
self.name = name
self.__price = price
@property
def price(self):
return self.__price * Goods.__discount
@classmethod #把一个方法 变成一个类中的方法 这个方法就直接可以被类调用,不需要任何依赖
def change_discount(cls,new_diccount): #修改折扣
cls.__discount = new_diccount
apple = Goods('苹果',5)
print(apple.price)
apple.change_discount(0.5)
print(apple.price)
执行结果:
4.0
2.5
3.@staticmethod 返回函数的静态方法。
该方法不强制要求传递参数,如下声明一个静态方法:
class C(object):
@staticmethod
def f(arg1, arg2, ...):
...
以上实例声明了静态方法 f,类可以不用实例化就可以调用该方法 C.f(),当然也可以实例化后调用 C().f()。
class C(object):
@staticmethod
def f():
print('runoob');
C.f(); # 静态方法无需实例化
cobj = C()
cobj.f() # 也可以实例化后调用
class Login:
def __init__(self,name,password):
self.name = name
self.pwd = password
def login(self):pass
@staticmethod
def get_usr_pwd(): # 静态方法 此处没有参数
usr = input('用户名 :')
pwd = input('密码 :')
Login(usr,pwd)
Login.get_usr_pwd()
在完全面向对象的程序中,如果一个函数即和对象没有关系 也和类也没有关系,那么就用staticmethod将这个函数变成一个静态方法
七、反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射),是用字符串类型的名字去操作变量。
反射使用的内置函数: hasattr、 getattr、 setattr、 delattr
hasattr和getattr成对出现
class A:
def fun(self):
print('in func')
a = A()
a.name = 'felix'
a.age = 30
1.反射对象的属性 getattr
ret = getattr(a,'name') #通过变量的字符串形式取到的值
print(ret)
print(a.__dict__)
变量名 = input('>>>') #func
print(getattr(a,变量名))
print(a.__dict__[变量名])
2.反射对象的方法 getattr
ret = getattr(a,'func')
ret()
3.反射类的属性 getattr
class A:
price = 20
@classmethod
def fun(self):
print('in func')
print(getattr(A,'price'))
4.反射类的方法 getattr
# A.fun()
if hasattr(A,'fun'):
getattr(A,'fun')()
# 模块
内置模块也能用反射
import time
print(time.strftime('%Y-%m-%d %H:%M:%S'))
print(getattr(time,'strftime')('%Y-%m-%d %H:%M:%S'))
import mokuai
# 反射模块的属性
# 普通方法
print(mokuai.day)
# getattr方法
print(getattr(mokuai,'day'))
#反射模块的方法
print(getattr(mokuai,'func')())
import sys
def func():
print('反射自己模块的变量')
name = 'felix'
print(sys.modules) #所有引用的模块
print(sys.modules['__main__']) #当前模块
print(sys.modules['__main__'].name) # #当前模块的变量 name
# 反射自己模块中的变量
print(getattr(sys.modules['__main__'],'name'))
# 反射自己模块中的方法(函数)
getattr(sys.modules['__main__'],'func')()
getattr(sys.modules[__name__],'func')()
# x = input('>>>')
# print(getattr(sys.modules['__main__'],x))
# setattr() 设置一个变量
class A:
pass
a = A()
setattr(A,'name','felix') #设置类的属性
setattr(a,'name','alina') #设置对象的属性
print(A.name)
print(a.name)
felix
alina
# delattr() 删除一个变量
delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix
print(a.name)
felix
delattr(A,'name')
print(a.name)
报错:
AttributeError: 'A' object has no attribute 'name'
# setattr() 设置一个变量
class A:
pass
a = A()
setattr(A,'name','felix') #设置类的属性
setattr(a,'name','alina') #设置对象的属性
print(A.name)
print(a.name)
felix
alina
# delattr() 删除一个变量
delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix
print(a.name)
felix
delattr(A,'name')
print(a.name)
报错:
AttributeError: 'A' object has no attribute 'name'
八、类的内置方法(专有方法)
1.__str__ --->>> str(obj)
2.__repr__ --->>> repr(obj)
class Teacher:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def __str__(self):
return "Teacher's object :%s" % self.name
def __repr__(self):
return str(self.__dict__)
def func(self):
return 'wahaha'
felix = Teacher('felix', 8888)
print(felix) # 打印一个对象的时候,就是调用a.__str__
print(str(felix))
print(repr(felix))
print('>>> %r' % felix)
print(felix.__str__)
执行结果:
Teacher's object :felix
Teacher's object :felix
{'name': 'felix', 'salary': 8888}
>>> {'name': 'felix', 'salary': 8888}
<bound method Teacher.__str__ of {'name': 'felix', 'salary': 8888}>
print(felix.__str__)
object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址
%s str() 直接打印 实际上都是走的__str__
%r repr() 实际上都是走的__repr__
repr 是str的备胎,但str不能做repr的备胎
小结:
1.print(obj) / '%s'%obj / str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
2.repr(),只会找__repr__,如果没有就找父类的
3.__len__ --->>> len()
class Classes:
def __init__(self,name):
self.name = name
self.student = []
def __len__(self):
return len(self.student)
python = Classes('Python3')
python.student.append('Felix')
python.student.append('Alina')
print(len(python))
执行结果:
2
4.__del__ --->>> 析构函数
class A:
def __del__(self): # 析构函数: 在删除一个对象之前进行一些收尾工作
self.f.close()
a = A()
a.f = open('文件名') # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中
del a # a.f 拿到了文件操作符消失在了内存中
del a # del 既执行了这个方法,又删除了变量
5.__call__ --->>> obj(obj)
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __call__(self):
for k in self.__dict__:
print(k, self.__dict__[k])
a = A('felix', 18)()
执行结果:
name felix
age 18
6.item
1.__getitem__
2.__setitem__
3.__delitem__
实例:
class Teacher:
def __init__(self,name,age):
self.name = name
self.age = age
def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
# del self.__dict__[key]
self.__dict__.pop(key)
def __delattr__(self, item): #
self.__dict__.pop(item)
felix = Teacher('felix',18)
print(felix.__dict__['name'])
felix.__dict__['sex'] = 'boy'
print(felix.sex,felix.__dict__['sex'])
print(felix.__dict__)
# del felix.age #object里的方法 原生支持 执行__delattr__方法 没有写__delattr__也不会报错
# 所以当没有__delitem__方法也不会报错 继承object
del felix['age'] #当没有实现__delitem__方法会报错
# print(felix.__dict__['age']) #会报错
print(felix.__dict__)
执行结果:
felix
boy boy
{'name': 'felix', 'age': 18, 'sex': 'boy'}
{'name': 'felix', 'sex': 'boy'}