面向对象
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个,类的内置装饰器
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!