面向过程:—侧重于怎么做?
1.把完的成某一个需求的 所有步骤 从头到尾 逐步实现
2.根据开发要求,将某些功能独立的代码封装成一个又一个函数
3.最后完成的代码,就是顺序的调用不同的函数
特点:
1.注重步骤与过程,不注重职责分工
2.如果需求复杂,代码会变得很复杂
3.开发复杂项目,没有固定的套路,开发难度很大
面向对象:–谁来做?
相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
1.在完成某一个需求前,首先确定职责–要做的事(方法)
2.根据职责确定不同的对象,在对象内部封装不同的方法(多个)
3.最后完成代码,就是顺序的让不同的对象调用不同的方法
特点:
1.注重对象和职责,不同的对象承担不同的职责
2.更加适合对复杂的需求变化,是专门应对复杂项目的开发,提供的固定套路
3.需要在面向过程的基础上,再学习一些面向对象的语法
1 类的概念
类:是对一群具有相同特征或行为的事物的一个统称,是抽象的,不能直接使用(就好比,飞机图纸不能直接飞上天)
特征:被称为属性
行为:被称为方法
类 就相当于制造飞机时的图纸,是一个模板,是负责创建对象的
1.1 类的创建
1.类名:满足这类事物的名字(大驼峰命名法)
大驼峰命名法:
1.每个单词的首字母大写
2.单词与单词之间没有下划线
2.属性:这个类创建出来的对象有什么样的特征
3.方法:这个类创建出来的对象有什么样的行为
类名的确定:
名词提炼法:分析整个业务流程,出现的名词,通常就是找到的类名
定义一个只包含方法的类
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
方法的定义格式和函数的几乎一样
区别在于第一个参数必须是self
示例:定义一个动物类()
class Animal: #类名
def eat(self): #方法1
pass
def dirnk(self): #方法2
pass
def run(self): #方法3
pass
def sleep(self): #方法4
pass
2 对象的概念
对象:是由类创建出来的一个具体的存在,可以直接使用(图纸创造出来的飞机,可以飞上天)
由哪一个类创建出来的对象,就拥有哪一个类的定义的属性和方法
“对象就相当于用图纸创造出来的飞机”
在程序开发中,应该先有类,再有对象
创建对象
当一个类定义完成之后,要使用这个类来创建对象
对象变量 = 类名()
示例:结合上一个例子
class Animal:
def eat(self):
print '吃'
def dirnk(self):
print '喝'
def run(self):
print '跑'
def sleep(self):
print '睡'
dog = Animal() #创建了一个狗的对象
dog.eat() #依次调用类中的方法
dog.dirnk()
dog.run()
dog.sleep()
#注意:当使用类名()创建对象的时候,python为对象在内存中分配空间,这也是这个对象生命周期的开始
1 引用和对象的关系
python中变量名和对象是分离的
首先从python的变量进行理解
在python中,如果要使用一个变量,不需要提前进行声明,只需要在用的时候,给这个变量赋值即可 (这个和C语言等静态类型语言不同,和python为动态类型有关)。
类比到面向对象:例如上面的创建dog这个对象的时候‘dog = Animal()‘ Animal()就是对象,dog就是引用。
第一个示例:
class Cat:
def eat(self):
print '小猫爱吃鱼'
def drink(self):
print '小猫爱喝水'
tom = Cat()
print tom #<__main__.Cat instance at 0x7fd641cf7518>
#tom引用指向猫这个对象,tom变量中记录的是猫这个对象在内存的地址(16位)
addr = id(tom) #python中的内置函数,以十进制方式显示对象的地址。
print addr ##140558203843864
这个示例说明:引用中存储的就是对象的内存地址
第二个示例:
class Cat:
def eat(self):
print '小猫爱吃鱼'
def drink(self):
print '小猫爱喝水'
tom = Cat() #之前的猫
lazy_cat = Cat() #现在的猫
print tom # <__main__.Cat instance at 0x7fd641cf7518>
print lazy_cat # <__main__.Cat instance at 0x7fd641cf7560>
addr = id(tom)
addr1 = id(lazy_cat)
print addr # 140558203843864
print addr1 # 140558203843936
分析这个示例的时候,可能都会有一个疑问?就是既然引用中存储是对象的地址,那么lazy_cat中存储的对象的地址就应该和之前那个tom里面地址是一样的。
但是实际的数值是不一样的,这就有两点原因:
1 说明了内存分配地址的时候,就是你创建对象的时候。之前tom引用这个对象的时候,内存给这个对象分配了空间,tom就记录这个地址,而在lazy_cat引用这个对象的时候,内存又重新给这个对象重新分配了地址,lazy_cat记录新的地址。
2 说明了同一个类可以创建不同的对象,尽管两个引用的地址不同,但是还是指向的同一个类。那有人会觉得这样会不会矛盾,不矛盾!python的解释机制就是翻译一行解释一行,解释一行,输出一行。那么在重新分配的时候tom里面内存其实已经被系统回收了!
1 初始化方法
初始化方法:
类名() 就可以创建一个对象
当使用类名()创建对象的时候,python解释器会自动执行以下操作
1.为对象在内存中分配空间 —创建对象
2.调用初始化方法
在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对_ init _方法进行改造
1.把希望设置的属性值,定义成_ init _ 方法的参数
2.在方法内部使用self.属性名 = 形参 接收外部传递的参数
3.在创建对象时,使用类名(属性1,属性2..)调用
class Cat:
def __init__(self, new_name):
# self.属性名 = 属性的初始值
# self.name = 'Tom'
self.name = new_name
# 在类中 任何方法都可以使用self.name
def eat(self):
print '%s 爱吃鱼' % self.name
tom = Cat('Tom')
print tom
print tom.name #Cat对象name属性
tom.eat() # Cat对象的eat行为
以上的结果就可以看出,self和tom拥有同一个地址,self可以理解为是tom的引用。注意: 只有需要从外界传递的参数, 才需要把这些参数定义成初始化方法的形参
在继承关系中,父类初始化已经好设置属性,子类也需要设置属性,那就必须在子类的初始化下,重新调用父类的初始化。
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaaa~'
self.hungry =False
else:
print 'No Thanks~'
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk!'
Bird.__init__(self) #子类中重新调用父类的初始化
def sing(self):
print self.sound
bird = Bird()
bird.eat()
littlebird = SongBird()
littlebird.eat()
littlebird.sing()
2_del_方法 和 _ str_方法,_ new _ 方法
2.1_del_方法用法
在python中
当使用类名()创建对象时,为对象分配完空间后,自动调用_ init_方法
当一个对象被从内存中销毁前(把这个对象从内存中删除掉),会自动调用 _ del_方法
应用场景
_ init_改造初始化方法,可以让创建对象更加灵活
_ del_如果希望在对象被销毁前,再做一些事情,可以考虑一下_ del_方法
class Cat():
def __init__(self, new_name):
self.name = new_name
print '%s 来了' % self.name
def __del__(self):
print '%s 走了' % self.name
# tom 是一个全局变量(s所以当我们的代码全部执行完之后,系统才会对tom 这个对象进行回收)
tom = Cat('Tom')
print tom.name
print '*' * 50
结果可以看出,系统在执行所有程序之后,会自动调去析构方法_ del _
class Cat():
def __init__(self, new_name):
self.name = new_name
print '%s 来了' % self.name
def __del__(self):
print '%s 走了' % self.name
# tom 是一个全局变量(s所以当我们的代码全部执行完之后,系统才会对tom 这个对象进行回收)
tom = Cat('Tom')
print tom.name
# del 关键字 可以删除一个对象 del关键字自己调用__del__方法
del tom
print '*' * 50
结果可以看出,出现del关键字会立刻调用_ del _ 方法。
有了_ del _ 这个概念之后,对象的生命周期就可以理解了。
生命周期(出生到死亡)
一个对象从调用类名()创建,生命周期开始
一个对象的__del__方法一但被调用,生命周期结束
在对象的生命周期内,可以访问对象属性,或者让对象调用方法
2.2_ str _ 方法
在python中,使用python输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)
希望使用print输出对象变量时,能够打印自定义的内容,就可以利用_ str_ 这个内置方法了
class Cat():
def __init__(self,name):
self.name = name
tom = Cat('Tom')
print tom
class Cat():
def __init__(self,name):
self.name = name
def __str__(self):
# 必须返回一个字符串
return '我是mini'
tom = Cat('Tom')
print tom
使用类名()创建对象时,python的解释器首先会调用new方法为对象分配空间
_ new _是一个由object基类提供的内置的静态方法,主要有两个作用:
在内存中为对象分配空间
返回对象的引用
python的解释器获得对象的引用后,将引用作为第一个参数,传递给_ init _方法。
_ new _:负责给对象分配空间 _ init _(初始化方法)负责给对象初始化
那么问题来了,如何重写_ new _ 方法呢?
我们之前的实验可以看出使用一个类创建对象的时候,创建不同的对象,系统就会为这个对象分配不同的地址,那如何让每次创建的对象都分配到相同的地址呢或者让_ init _ 只执行一次?
举一个例子:
class MusicPlsyer(object):
instance = None
def __new__(cls, *args, **kwargs):
# 第一个参数cls:哪一个类调用就传递哪一个类
# 第二个参数*args:多值的元组参数
# 第三个参数**kwargs:多值的字典参数
# 1.创建对象的时候,new方法会被自动调用
print '创建对象,分配空间' # 重写了父类的方法
# 2.为对象分配空间
# 注意:__new__方法是一个静态方法,在调用的时候,第一个参数为cls
if cls.instance is None:
# 调用父类的方法,为第一个对象分配空间
cls.instance = object.__new__(cls)
# 3.返回对象的引用
return cls.instance
def __init__(self):
print '初始化播放器'
# 创建播放器对象
player1 = MusicPlsyer()
print player1
player2 = MusicPlsyer()
print player2
举第二个例子:如何让初始化方法也执行一次
class MusicPlayer(object):
instance = None
init_flag = False
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = object.__new__(cls)
return cls.instance
def __init__(self):
# 1.判断是否执行过初始化动作
if MusicPlayer.init_flag:
return
# 2.如果没有执行过,再执行初始化动作
print '初始化方法'
MusicPlayer.init_flag = True
player1 = MusicPlayer()
print player1
player2 = MusicPlayer()
print player2
3 map 函数
map():python内置函数,接收两个参数,一个是函数,一个是序列
map依次将序列传递给函数,并把结果作为新的列表返回
def f(x):
return x*x
print map(lambda x:x*x,[1,2,3,4,5])
4 私有属性和私有方法
在实际开发中,对象的某些属性或方法可能只希望在对象的内部使用,而不希望在外部被访问到
私有属性 就是 对象 不希望公开的 属性
私有方法 就是 对象 不希望公开的 方法
4.1 普通类的私有属性和私有方法
class Men:
def __init__(self, name):
self.name = name
self.__age = 19 ##双下划线表示私有属性
def __secret(self): ##双下划线表示私有方法
print '%s的年龄是%d' % (self.name, self.__age)
Bob = Men('Bob')
Bob.secret() #私有方法,不允许在外界直接访问
class Men:
def __init__(self, name):
self.name = name
self.__age = 19 ##双下划线表示私有属性
def _secret(self): ##在对象的方法内部,是可以访问对象的私有属性的
print '%s的年龄是%d' % (self.name, self.__age)
Bob = Men('Bob')
Bob._secret()
4.2 继承私有属性和方法
如果有A和B两个类,A是父类,B是子类,如果需要从子类来读取被隐藏在父类的代码,怎么办?
你会发现:
1 在子类的对象方法中,不能访问父类的私有属性
2 在子类的对象方法中,不能调用父类的私有方法
3 在外界可以访问父类的公有属性和调用公有方法
4 在外界不能直接访问对象的私有属性/调用私有方法
5 在子类方法的内部能访问父类的公有属性和调用父类的公有方法
现在就有一种思想:
我们就可以在父类A中建立一个共有方法,在这个共有方法里面调用父类A中的私有属性和方法。然后再通过子类B来调用A类的共有方法来解决这个问题
示例:
class A(object):
def __init__(self):
self.num1 = 100
self.__num2 = 200 #私有属性
def __text(self): #私有方法
print '私有方法%d %d' % (self.num1, self.__num2)
def test(self): #建立的共有方法
print '父类的公有方法 %d' % self.__num2
self.__text()
class B(A):
def demo(self):
print '子类方法 %d' % self.num1
self.test() #子类调用
b = B()
print b
b.demo()
一切皆对象
类也是一个特殊的对象 –类对象
类对象可以拥有自己的属性和方法
1.类属性
2.类方法
4.3.1 类属性
类属性和实例属性
概念和使用
类属性就是给类对象定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征
比如有这样一个需求:
定义一个工具类
每件工具都有自己的name
知道使用这个类,创建了多少个工具对象
示例:
class Tool(object):
# 1.使用赋值语句定义类属性,记录所有的工具数量
count = 0
def __init__(self,name):
self.name = name
# 让类属性的值 +1
Tool.count += 1
# 创建工具对象(对象在创建的时候,会自动调用初始化方法)
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('水桶')
# 输出工具对象的总数
# 使用 类名.count 来获取属性名
print Tool.count
4.3.2 类方法
类属性就是针对类对象定义的属性
使用赋值语句在class关键字下方可以定义类属性
类属性用于记录与这个类相关的特性
类方法就是针对类对象定义的方法
在类方法内部就可以直接访问类属性或调用类方法
class Toy(object):
# 使用赋值语句定义类属性,记录所有玩具的数量
count = 0
@classmethod
def show_toy_count(cls):
# 在类方法的内部,可以直接访问类属性或调用类方法
print '玩具对象的数量 %d' % cls.count
def __init__(self,name):
self.name = name
Toy.count += 1
# 创建玩具对象
toy1 = Toy('乐高')
toy2 = Toy('玩具车')
# 调用类方法
Toy.show_toy_count()
在开发的时候,如果需要在类中封装一个方法,这个方法
既不需要访问实例属性或者调用实例方法
也不需要访问那类属性或者调用类方法
这个时候可以把这个方法封装成一个静态方法
class Cat(object):
@staticmethod
# 不访问实例属性/类属性
# 静态方法不需要传递第一个参数self
def call():
print 'skr!'
# 通过类名. 调用静态方法
# 不需要创建对象,就可以直接调用
Cat.call()
5 新式类和旧式类
5.1 定义
object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
新式类:以object为基类的类,推荐使用
经典类:不以object为基类的类,不推荐使用
在python3.X中定义的类时,如果没有指定父类,会默认使用object作为基类–python3.x中定义的类都是新式类
在python2.x中定义类时,如果没有指定父类,则不会以object作为基类
class A(object):
pass
a = A()
print dir(a) # 查看内置的属性和方法
class B:
pass
b = B()
print dir(b)
新式类旧式类最明显的区别在于继承搜索的顺序发生了改变
即:
经典类多继承搜索顺序(深度优先):
先深入继承树左侧查找,然后再返回,开始查找右侧
新式类多继承搜索顺序(广度优先):
先在水平方向查找,然后再向上查找。