面向对象
- 类概念
- 类的定义
- 类的实例化
- 类和实例的属性
- 类的私有变量
- 数据封装
- 继承
- 多态
- 多继承
- 类的特殊方法
- 装饰器
- 类的装饰器
1.面向对象(Object Oriented,OO)概念
面向对象,是我们编程的一种思维。
早期的计算机编程是基于面向过程的方法,例如实现算术运算1+1+2 = 4,通过设计一个算法就可以解决当时的问题。随着计算机技术的不断提高,计算机被用于解决越来越复杂的问题。通过面向对象的方式,将现实世界的事物抽象成对象,现实世界中的关系抽象成类、继承。通过面向对象的方法,更利于用人理解的方式,对复杂系统进行分析、设计与编程。同时也提高了系统的可维护性,可扩展性,可重用性。
(就是使编程的思维,更接近与人的思维和认知)
面向对象编程的关键,就是类的定义。
类是对现实生活中一类具有共同特征的事物的抽象。
2.类的定义
基本形式:
class ClassName(object):
Statement
1.class定义类的关键字
2.ClassName类名,类名的每个单词的首字母大写(驼峰规则)。
3.object是父类名,object是一切类的基类。在python3中如果继承类是基类可以省略不写。
'''
class Animal(object):
pass
'''
'''
重点:学会定义类,了解属性和方法,类的初始化和实例化。
定义类时,这种方法可以使类对象实例按某种特定的模式生产出来。
'''
#类方法:
后面的参数中第一个参数我们约定俗成的为self参数名,
self代表的是在类实例化后这个实例对象本身。
'''
class Animal:
#构造方法,实例化对象,必须调用__init__
#在初始化的时候,默认往构造方法,传入一个值
#self是实例化对象本身
def __init__(self):
print("正在实例化一个对象")
def test(self):#实例化对象调用方法,必须加上self
print("aaa")
def test1(self,b):#第一个参数默认是self参数,不需要传值
print("方法;test1,self=%s"%(type(self)))
def test2():#未加self参数,只能通过 Animal.test2()直接调用,实例化对象无法调用
print("方法;test2")
运行结果:
>>> Animal().test1(1111)
正在实例化一个对象
方法;test1,self=<class '__main__.Animal'>
'''
'''
class Animal: #当我们没有写__init__(),默认调用我们父类的__init__
def test(self):
print("aaa")
'''
初始化函数除了有self这个参数表示实例对象本身之外,
其他的参数的定义也遵循函数的必备参数和默认参数一样的原则,
必备参数就是在实例化是一定要传入的参数,
默认参数就是在定义时可以给这个参数一个初始值。没有函数名的函数
3.类的实例化
基本形式:实例对象名 = 类名(参数)
在实例化的过程中,self代表的就是这个实例对象自己。
实例化时会把类名后面接的参数传进去赋值给实例,
这样传进去的参数就成为了这个实例对象的属性。
实例化的过程遵循函数调用的原则。
在实例化时也必须个数和顺序与定义时相同(使用关键字参数可以改变传参的顺序)。
当初始化函数定义时使用了默认参数时,在实例化时默认参数可以不传参这时
这个实例对象就会使用默认的属性,如果传了参数进去则会改变这参数值,
使实例化对象的属性就为你传进来的这个参数。
isinstance(实例名,类名)
判断一个实例是不是这个类的实例。
4.类和实例的属性
类属性
.类属性是可以直接通过“类名.属性名”来访问和修改。
.类属性是这个类的所有实例对象所共有的属性,
任意一个实例对象都可以访问并修改这个属性(私有隐藏除外)。
.对类属性的修改,遵循基本数据类型的特性:列表可以直接修改,字符串不可以,
所以当类属性是一个列表时,通过任意一个实例对象对其进行修改。
但字符串类型的类属性不能通过实例对象对其进行修改。
当实例对不可变对象进行修改之后,会查找实例的类属性,不会查找类的属性,同时类的属性不会边
实例属性
.在属性前面加了self标识的属性为实例的属性。
.在定义的时候用的self加属性名字的形式,在查看实例的属性时
就是通过实例的名称+‘.’+属性名来访问实例属性。
'''
class Aniaml:
eye=2 #共有的属性
def __init__(self,name,food):
print("正在实例化")
self.name=name #实例化属性
self.food=food
def getName(self):
print(self.name)
运行:
>>> dog=Aniaml("大黄","骨头")
实例化属性调用:
>>> dog.name #调用实例属性
'大黄'
类属性调用:
>>>Aniaml.eye
>>> dog.eye
'''
一些说明:
.一般,方法第一个参数被命名为self,,这仅仅是一个约定,
self没有特殊含义,程序员遵循这个约定。
.查看类中的属性和实例属性可以调用__dict__方法返回属性组成的字典。
.Python中属性的获取是按照从下到上的顺序来查找属性
.Python中的类和实例是两个完全独立的对象
.Python中的属性设置是针对对象本身进行的
5.类的私有属性和方法
在Python中,通过单下划线”_”来实现模块级别的私有化,
一般约定以单下划线”_”开头的变量、函数为模块私有的,
也就是说”from moduleName import *”将不会引入以单下划线”_”开头的变量、函数
'''
import random #显示所有的方法,属性
from random import * #只显示
'''
对于Python中的类属性,可以通过双下划线”__”来实现一定程度的私有化。
_”和” __”的使用 更多的是一种规范/约定,并没有真正达到限制的目的:
“_”:以单下划线开头只能允许其本身与子类进行访问,(起到一个保护的作用)
“__”:双下划线的表示的是私有类型的变量。这类属性在运行时属性名会加上单下划线和类名。
“__foo__”:以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如 __init__()
'''
class Aniaml:
eye=2
_age=3
__leg=4
def __init__(self,name,food):
self.name=name
self.food=food
def getName(self):
print(self.name)
>>> dog._age #单_会隐藏属性名称
>>> dog._Aniaml__leg #双_会隐藏属性名称,双_会修改属性的名称
'''
6.数据封装
在类里面数据属性和行为函数的形式封装起来,
访问时直接调用,不需知道类里面具体的实现方法。 比如,list.append
封装:
def test2():
print("方法;test2")
7.继承
用法:
.在定义类时,可以从已有的类继承,
被继承的类称为基类(父类),新定义的类称为派生类(子类)。
.在类中找不到调用的属性时就搜索基类,
如果基类是从别的类派生而来,这个规则会递归的应用上去。
反过来不行。
.如果派生类中的属性与基类属性重名,那么派生类的属性会覆盖掉基类的属性。
包括初始化函数。
.派生类在初始化函数中需要继承和修改初始化过程,
使用’类名+__init__(arg)’来实现继承和私有特性,也可以使用super()函数。
issubclass(类名1,类名2)
判断类1是否继承了类2
作用:
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
继承完全可以理解成类之间的类型和子类型关系。
子类在重写父类方法之后,如果要继承父类方法中的功能,要先调用父类的方法 class.fun(self)
'''
class Aniaml:
eye=2
_age=3
__leg=4
def __init__(self,name,food):
self.name=name
self.food=food
def getName(self):
print(self.name)
class People(Aniaml):#继承
__leg=2
def __init__(self,name,food,sex):
self.name=name
self.food=food
self.sex=sex
def getSex(self):
print(self.sex)
'''
8.多态
当派生类重写了基类的方法时就实现了多态性。(子类重写父类方法)
class Aniaml:
eye=2
_age=3
__leg=4
def __init__(self,name,food):
self.name=name
self.food=food
def getName(self):
print(self.name)
class People(Aniaml):
__leg=2
def __init__(self,name,food,sex):
self.name=name
self.food=food
self.sex=sex
def getSex(self):
print(self.sex)
def speak(self):
print("adsfsfd")
class Chinese(People):#中国人
def speak(self):#中国文说你好
print("你好"
class America(People):#美国人
def speak(self):#美国人说Hello
print("Hello")
class Thai(People):#泰国人
def speak(self):#泰国人说萨瓦迪卡
print("萨瓦迪卡")
xiaomi=Chinese("小明","米饭","男")
jack=America("jack","面包","男")
lala=Thai("lala","香蕉","未知")
9.多继承
#当继承的类有同种方法的时候,只会继承前面一个的方法。调用父类方法super()
class Base:
def play(self):
print("This is base")
class A(Base):
def play(self):
print(type(self))
print("This is a")
class B(Base):
def play(self):
print("This is b")
class C(A,B):
def play(self):
#A.play(self) #第一种调用父类的方法
super().play()#第二种调用父类的方法,同super(C,self).play()
#super(C,self).play()
print("This is c")
运行效果:
<class '__main__.C'>
This is a
This is c
#对象的扩展使用
#C().__class__.mro() 查看对象的排序
#C(B,A) C->B->A->Base->object 针对C继承对象排序
#C(A,B) C->A->B->Base->object 针对C继承对象排序
class C(B,A):
def play(self):
#super(A,self).play() #调用base
super(B,self).play() #调用a
>>> C().__class__.mro() #C->B->A->Base->object 针对C继承对象排序
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.Base'>, <class 'object'>]
10.类的特殊方法
千万不要把自己往死胡同里面逼,否则你会走火入魔
'''
class Rectangle:
'''测试'''
aaa=1
def __init__(self,length,width):
if isinstance(length,(int,float)) and isinstance(width,(int,float)):
self.length=length
self.width=width
else:
print("请输入int or float")
def area(self):
return self.length*self.width
def __str__(self):
return "这个长方形的面积%s"%self.area()
def __repr__(self):
return "长:%s 宽:%s"%(self.length,self.width)
def __call__(self):
print("这是一个Rectangle类,你要干嘛")
def __add__(self,other):
return self.area()+other.area()
def __sub__(self,other):
return self.area()-other.area()
r=Rectangle(2,3)
r1=Rectangle(2,3)
r2=Rectangle(2,3)
'''
#类属性:
__dict__ # 类的属性(包含一个字典,由类的数据属性组成)
>>> r.__dict__
{'length': 2, 'width': 3}
>>> r.aaa=33
>>> r.__dict__
#注意,共有属性,当不修改时,默认引用Rectangle
#修改之后,才会出现再实例里面
{'length': 2, 'width': 3, 'aaa': 33}
__doc__ # 类的文档字符串
>>> r.__doc__
'测试'
#类方法:(魔法方法) ,方法也是对象
__init__ # 初始化
__repr__ # 直接返回这个对象 repr() 函数就是调用对象的这个方法
>>> r
长:2 宽:3
__str__ # print(obj) 如果类里面定义了__repr__,没有定义__str__ print(obj)也会返回__repr__的内容,或者说__repr__的优先级更高
>>> print(r) #重写print方法了
这个长方形的面积6
"%s"%"ssss" 对应 __str__
"%r"%"rrrr" 对应 __repr__
__call__ # Obj() 使实例可被调用
>>> r()
这是一个Rectangle类,你要干嘛
#运算符方法
__add__(self,other) #x+y
>>> r1+r2
12
__sub__(self,other) #x-y
__mul__(self,other) #x*y
__mod__(self,other) #x%y
__iadd__(self,other) #x+=y
__isub__(self,other) #x-=y
__radd__(self,other) #y+x
__rsub__(self,other) #y-x
__imul__(self,other) #x*=y
__imod__(self,other) #x%=y
#和类有关的几个函数
delattr() # 删除对象属性
getattr() # 得到某个属性值
setattr() # 给对象添加某个属性值
hasattr() # 判断对象object是否包含名为name的特性
isinstance() # 检查对象是否是类的对象,返回True或False
issubclass() # 检查一个类是否是另一个类的子类。返回True或False
11.装饰器
装饰器(deco):
装饰函数的参数是被装饰的函数对象,返回原函数对象装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
def f1(func):
print("f1 running")
def f2(y):
print("f2 running")
return func(y)+1
return f2
@f1
def gun2(m):
print("gun running")
return m*m
#运行结果
f1 running
>>> gun2(5)
f2 running
gun running
26
运行流程
1.@f1->f1(gun2)->f2
2.f2,等待调用
3.gun2(2)->当参数5传入->f2(5)
4.f2(5),开始运行->print("f2 running")->fun(y):func=gun2 y=5
5.gun2(5) 开始运行->print("gun running")->25
6.25+1=26
#测试时间的装饰器
import time #不要纠结
def run_time(func):
def new_fun():
t0 = time.time()
print('star time: %s'%(time.strftime('%x',time.localtime())) )
func()
print('end time: %s'%(time.strftime('%x',time.localtime())) )
print('run time: %s'%(time.time() - t0))
return new_fun
@run_time
def test():
for i in range(1,10):
for j in range(1,i+1):
print('%dx%d=%2s'%(j,i,i*j),end = ' ')
print ()
12.类装饰器
'''
class Rectangle:
'''测试'''
aaa=1
def __init__(self,length,width):
if isinstance(length,(int,float)) and isinstance(width,(int,float)):
self.length=length
self.width=width
else:
print("请输入int or float")
@property #可以把方法当属性使用
def area(self):
return self.length*self.width
@staticmethod #把方法变成静态方法
def func():
print("可以调用")
@classmethod #把实例化对象转换成类
def show(self):
print(self)
print("show fun")
>>> Rectangle(2,3).area
6
'''
@property
装饰过的函数返回的不再是一个函数,而是一个property对象
装饰过后的方法不再是可调用的对象,可以看做数据属性直接访问。
@staticmethod #(静态方法)
把没有参数的函数装饰过后变成可被实例调用的函数,
函数定义时是没有参数的,可以不接收参数
@classmethod (类方法)
把装饰过的方法变成一个classmethod类对象,既能能被类调用又能被实例调用。
注意参数是cls代表这个类本身。而是用实例的方法只能被实例调用。
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁
'''
#类装饰器
'''
class Test_Class():
def __init__(self,func):
self.func=func
def __call__(self):
print("类")
return self.func
@Test_Class
def fun_test():
print("这只是一个测试")
运行:
>>> fun_test()
类
<function fun_test at 0x033081E0>
>>> fun_test()()
类
这只是一个测试
#python自带的3个,类的内置装饰器