需要了解的定义性文字:
面向对象(object-oriented ;简称: OO) 至今还没有统一的概念 我这里把它定义为: 按人们 认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、设 计、实现软件的办法。
面向对象编程(Object Oriented Programming-OOP) 是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
定义一个类,格式如下:
class 类名:
方法列表
demo:定义一个Car类
# 定义类
class Car:
# 方法
def getCarInfo(self):
print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color))
def move(self):
print("车正在移动...")
说明:
通过上一节课程,定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了
python中,可以根据已经定义的类去创建出一个个对象
创建对象的格式为:
对象名 = 类名()
创建对象demo:
# 定义类
class Car:
# 移动
def move(self):
print('车在奔跑...')
# 鸣笛
def toot(self):
print("车在鸣笛...嘟嘟..")
# 创建一个对象,并用变量BMW来保存它的引用
BMW = Car()
BMW.color = '黑色'
BMW.wheelNum = 4 #轮子数量
BMW.move()
BMW.toot()
print(BMW.color)
print(BMW.wheelNum)
__init__()
方法想一想:
在上一小节的demo中,我们已经给BMW这个对象添加了2个属性,wheelNum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?
答:
__init__()
方法
def 类名:
#初始化函数,用来完成一些默认的设定
def __init__():
pass
__init__()
方法的调用
# 定义汽车类
class Car:
def __init__(self):
self.wheelNum = 4
self.color = '蓝色'
def move(self):
print('车在跑,目标:夏威夷')
# 创建对象
BMW = Car()
print('车的颜色为:%s'%BMW.color)
print('车轮胎数量为:%d'%BMW.wheelNum)
总结1
当创建Car对象后,在没有调用
__init__()
方法的前提下,BMW就默认拥有了2个属性wheelNum和color,原因是__init__()
方法是在创建对象后,就立刻被默认调用了
如果把BMW使用print进行输出的话,会看到如下的信息
即看到的是创建出来的BMW对象在内存中的地址
__str__()
方法class Car:
def __init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def __str__(self):
msg = "嘿。。。我的颜色是" + self.color + "我有" + int(self.wheelNum) + "个轮胎..."
return msg
def move(self):
print('车在跑,目标:夏威夷')
BMW = Car(4, "白色")
print(BMW)
总结
__xxxx__()
的,那么就有特殊的功能,因此叫做“魔法”方法__str__(self)
方法,那么就会打印从在这个方法中return的数据
看如下示例:
# 定义一个类
class Animal:
# 方法
def __init__(self, name):
self.name = name
def printName(self):
print('名字为:%s'%self.name)
# 定义一个函数
def myPrint(animal):
animal.printName()
dog1 = Animal('西西')
myPrint(dog1)
dog2 = Animal('北北')
myPrint(dog2)
运行结果:
可能你已经意识到,查看过着修改对象的属性(数据),有2种方法
SweetPotato.cookedLevel = 5
SweetPotato.cook(5)
明明可以使用第1种方法直接修改,为什么还要定义方法来间接修改呢?
至少有2个原因:
cookedLevel = -3
。这会使地瓜比以前还生,当然了这也没有任何意义,通过使用方法来进行修改,就可以在方法中进行数据合法性的检查
如果有一个对象,当需要对其进行修改属性时,有2种方法
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
class People(object):
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, newName):
if len(newName) >= 5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("dongGe")
print(xiaoming.__name)
class People(object):
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, newName):
if len(newName) >= 5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("dongGe")
xiaoming.setName("wanger")
print(xiaoming.getName())
xiaoming.setName("lisi")
print(xiaoming.getName())
总结
__del__()
方法创建对象后,python解释器默认调用__init__()
方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()
方法
import time
class Animal(object):
# 初始化方法
# 创建完对象后会自动被调用
def __init__(self, name):
print('__init__方法被调用')
self.__name = name
# 析构方法
# 当对象被删除时,会自动被调用
def __del__(self):
print("__del__方法被调用")
print("%s对象马上被干掉了..."%self.__name)
# 创建对象
dog = Animal("哈皮狗")
# 删除对象
del dog
cat = Animal("波斯猫")
cat2 = cat
cat3 = cat
print("---马上 删除cat对象")
del cat
print("---马上 删除cat2对象")
del cat2
print("---马上 删除cat3对象")
del cat3
print("程序2秒钟后结束")
time.sleep(2)
结果:
总结
在现实生活中,继承一般指的是子女继承父辈的财产,如下图
# 定义一个父类,如下:
class Cat(object):
def __init__(self, name, color="白色"):
self.name = name
self.color = color
def run(self):
print("%s--在跑"%self.name)
# 定义一个子类,继承Cat类如下:
class Bosi(Cat):
def setNewName(self, newName):
self.name = newName
def eat(self):
print("%s--在吃"%self.name)
bs = Bosi("印度猫")
print('bs的名字为:%s'%bs.name)
print('bs的颜色为:%s'%bs.color)
bs.eat()
bs.setNewName('波斯')
bs.run()
运行结果:
说明:
__init__
方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建Bosi的对象,就默认执行了那个继承过来的__init__
方法总结
class Animal(object):
def __init__(self, name='动物', color='白色'):
self.__name = name
self.color = color
def __test(self):
print(self.__name)
print(self.color)
def test(self):
print(self.__name)
print(self.color)
class Dog(Animal):
def dogTest1(self):
#print(self.__name) #不能访问到父类的私有属性
print(self.color)
def dogTest2(self):
#self.__test() #不能访问父类中的私有方法
self.test()
A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()
print("------分割线-----")
D = Dog(name = "小花狗", color = "黄色")
D.dogTest1()
D.dogTest2()
Python中多继承的格式如下:
# 定义一个父类
class A:
def printA(self):
print('----A----')
# 定义一个父类
class B:
def printB(self):
print('----B----')
# 定义一个子类,继承自A、B
class C(A,B):
def printC(self):
print('----C----')
obj_C = C()
obj_C.printA()
obj_C.printB()
运行结果:
----A----
----B----
说明
如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
#coding=utf-8
class base(object):
def test(self):
print('----base test----')
class A(base):
def test(self):
print('----A test----')
# 定义一个父类
class B(base):
def test(self):
print('----B test----')
# 定义一个子类,继承自A、B
class C(A,B):
pass
obj_C = C()
obj_C.test()
print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
#coding=utf-8
class Cat(object):
def sayHello(self):
print("halou-----1")
class Bosi(Cat):
def sayHello(self):
print("halou-----2")
bosi = Bosi()
bosi.sayHello()
#coding=utf-8
class Cat(object):
def __init__(self,name):
self.name = name
self.color = 'yellow'
class Bosi(Cat):
def __init__(self,name):
# 调用父类的__init__方法1(python2)
#Cat.__init__(self,name)
# 调用父类的__init__方法2
#super(Bosi,self).__init__(name)
# 调用父类的__init__方法3
super().__init__(name)
def getName(self):
return self.name
bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态
class F1(object):
def show(self):
print 'F1.show'
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象
def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
print obj.show()
s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
class F1(object):
def show(self):
print 'F1.show'
class S1(F1):
def show(self):
print 'S1.show'
class S2(F1):
def show(self):
print 'S2.show'
def Func(obj):
print obj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
在了解了类基本的东西之后,下面看一下python中这几个概念的区别
先来谈一下类属性
和实例属性
在前面的例子中我们接触到的就是实例属性(对象属性),顾名思义,类属性就是类对象
所拥有的属性,它被所有类对象
的实例对象
所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象
和实例对象
访问
class People(object):
name = 'Tom' #公有的类属性
__age = 12 #私有的类属性
p = People()
print(p.name) #正确
print(People.name) #正确
print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
class People(object):
address = '山东' #类属性
def __init__(self):
self.name = 'xiaowang' #实例属性
self.age = 20 #实例属性
p = People()
p.age =12 #实例属性
print(p.address) #正确
print(p.name) #正确
print(p.age) #正确
print(People.address) #正确
print(People.name) #错误
print(People.age) #错误
class People(object):
country = 'china' #类属性
print(People.country)
p = People()
print(p.country)
p.country = 'japan'
print(p.country) #实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country #删除实例属性
print(p.country)
类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。
是类对象所拥有的方法,需要用修饰器@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
class People(object):
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
p = People()
print p.getCountry() #可以用过实例对象引用
print People.getCountry() #可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改:
class People(object):
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = People()
print p.getCountry() #可以用过实例对象引用
print People.getCountry() #可以通过类对象引用
p.setCountry('japan')
print p.getCountry()
print People.getCountry()
结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变
需要通过修饰器@staticmethod
来进行修饰,静态方法不需要多定义参数
class People(object):
country = 'china'
@staticmethod
#静态方法
def getCountry():
return People.country
print People.getCountry()
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用
# 定义伊兰特车类
class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类
class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 用来生成具体的对象
def createCar(typeName):
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
# 定义一个销售北京现代车的店类
class CarStore(object):
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
car = createCar(typeName)
return car
# 定义伊兰特车类
class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类
class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个生产汽车的工厂,让其根据具体的订单生产车
class CarFactory(object):
def createCar(self,typeName):
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
# 定义一个销售北京现代车的店类
class CarStore(object):
def __init__(self):
#设置4s店的指定生产汽车的工厂
self.carFactory = CarFactory()
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
car = self.carFactory.createCar(typeName)
return car
咋一看来,好像只是把生产环节重新创建了一个类,这确实比较像是一种编程习惯,此种解决方式被称作简单工厂模式
工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,4s店只负责销售,汽车厂只负责制造
当买车时,有很多种品牌可以选择,比如北京现代、别克、凯迪拉克、特斯拉等,那么此时该怎样进行设计呢?
# 定义一个基本的4S店类
class CarStore(object):
#仅仅是定义了有这个方法,并没有实现,具体功能,这个需要在子类中实现
def createCar(self, typeName):
pass
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
self.car = self.createCar(typeName)
self.car.move()
self.car.stop()
# 定义一个北京现代4S店类
class XiandaiCarStore(CarStore):
def createCar(self, typeName):
self.carFactory = CarFactory()
return self.carFactory.createCar(typeName)
# 定义伊兰特车类
class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类
class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个生产汽车的工厂,让其根据具体得订单生产车
class CarFactory(object):
def createCar(self,typeName):
self.typeName = typeName
if self.typeName == "伊兰特":
self.car = YilanteCar()
elif self.typeName == "索纳塔":
self.car = SuonataCar()
return self.car
suonata = XiandaiCarStore()
suonata.order("索纳塔")
工厂方法模式
的定义定义了一个创建对象的
接口
(可以理解为函数),但由子类决定要实例化的类是哪一个,工厂方法模式让类的实例化推迟到子类,抽象的CarStore提供了一个创建对象的方法createCar,也叫作工厂方法
。子类真正实现这个createCar方法创建出具体产品。 创建者类不需要直到实际创建的产品是哪一个,选择了使用了哪个子类,自然也就决定了实际创建的产品是什么。
__new__
方法__new__和__init__
的作用class A(object):
def __init__(self):
print("这是 init 方法")
def __new__(cls):
print("这是 new 方法")
return object.__new__(cls)
A()
总结
__new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
__new__
必须要有返回值,返回实例化出来的实例,这点在自己实现__new__
时要特别注意,可以return父类__new__
出来的实例,或者直接是object的__new__
出来的实例
__init__
有一个参数self,就是这个__new__
返回的实例,__init__
在__new__
的基础上可以完成一些其它初始化的动作,__init__
不需要返回值
我们可以将类比作制造商,__new__
方法就是前期的原材料购买环节,__init__
方法就是在有原材料的基础上,加工,初始化商品环节
举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
# 实例化一个单例
class Singleton(object):
__instance = None
def __new__(cls, age, name):
#如果类数字能够__instance没有或者没有赋值
#那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
#能够知道之前已经创建过对象了,这样就保证了只有1个对象
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton(18, "dongGe")
b = Singleton(8, "dongGe")
print(id(a))
print(id(b))
a.age = 19 #给a指向的对象添加一个属性
print(b.age)#获取b指向的对象的age属性
运行结果:
In [12]: class Singleton(object):
...: __instance = None
...:
...: def __new__(cls, age, name):
...: if not cls.__instance:
...: cls.__instance = object.__new__(cls)
...: return cls.__instance
...:
...: a = Singleton(18, "dongGe")
...: b = Singleton(8, "dongGe")
...:
...: print(id(a))
...: print(id(b))
...:
...: a.age = 19
...: print(b.age)
...:
4391023224
4391023224
19
# 实例化一个单例
class Singleton(object):
__instance = None
__first_init = False
def __new__(cls, age, name):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, age, name):
if not self.__first_init:
self.age = age
self.name = name
Singleton.__first_init = True
a = Singleton(18, "dongGe")
b = Singleton(8, "dongGe")
print(id(a))
print(id(b))
print(a.age)
print(b.age)
a.age = 19
print(b.age)
运行结果:
有过C语言编程经验的朋友都知道在C语言中如果要引用sqrt函数
,必须用语句#include
引入math.h这个头文件,否则是无法正常进行调用的。
那么在Python中,如果要引用一些其他的函数,该怎么处理呢?
在Python中有一个概念叫做模块(module),这个和C语言中的头文件以及Java中的包很类似,比如在Python中要调用sqrt函数
,必须用import关键字引入math这个模块,下面就来了解一下Python中的模块。
说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块
在Python中用关键字import
来引入某个模块,比如要引用模块math,就可以在文件最开始的地方用import math来引入。
形如:
import module1,mudule2...
当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。
在调用math模块中的函数时,必须这样引用:
模块名.函数名
想一想:
为什么必须加上模块名调用呢?
答:
因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名
import math
#这样会报错
print sqrt(2)
#这样才能正确输出结果
print math.sqrt(2)
有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:
from 模块名 import 函数名1,函数名2....
不仅可以引入函数,还可以引入一些全局变量、类等
注意:
通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。
如果想一次性引入math中所有的东西,还可以通过from math import *来实现
Python的from语句让你从模块中导入一个指定的部分到当前命名空间中
语法如下:
from modname import name1[, name2[, ... nameN]]
例如,要导入模块fib的fibonacci函数,使用如下语句:
from fib import fibonacci
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
In [1]: import time as tt
In [2]: time.sleep(1)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 time.sleep(1)
NameError: name 'time' is not defined
In [3]:
In [3]:
In [3]: tt.sleep(1)
In [4]:
In [4]:
In [4]: from time import sleep as sp
In [5]: sleep(1)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in ()
----> 1 sleep(1)
NameError: name 'sleep' is not defined
In [6]:
In [6]:
In [6]: sp(1)
In [7]:
当你导入一个模块,Python解析器对模块位置的搜索顺序是:
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。
比如有这样一个文件test.py,在test.py中定义了函数add
test.py
def add(a,b):
return a+b
那么在其他文件中就可以先import test,然后通过test.add(a,b)来调用了,当然也可以通过from test import add来引入
main.py
import test
result = test.add(11,22)
print(result)
在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息,例如:
test.py
def add(a,b):
return a+b
# 用来进行测试
ret = add(12,22)
print('int test.py file,,,,12+22=%d'%ret)
如果此时,在其他py文件中引入了此文件的话,想想看,测试的那段代码是否也会执行呢!
main.py
import test
result = test.add(11,22)
print(result)
运行现象:
至此,可发现test.py中的测试代码,应该是单独执行test.py文件时才应该执行的,不应该是其他的文件中引用而执行
为了解决这个问题,python在执行一个文件时有个变量__name__
总结:
__all__
__init__.py
文件,那么这个文件夹就称之为包
__init__.py
文件有什么用__init__.py
控制着包的导入行为
__init__.py
为空仅仅是把这个包导入,不会导入包中的模块
__all__
在__init__.py
文件中,定义一个__all__
变量,它控制着 from 包名 import *时导入的模块
__init__.py
文件中编写内容可以在这个文件中编写语句,当导入时,这些语句就会被执行