1 面向对象
面向过程是一件事“该怎么做”,面向对象是一件事“该让谁来做”,那个“谁”就是对象,他要怎么做是他自己的事,反正最后一群对象合力能把事做好。
类和对象
对象是面向对象的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出另外一个新的概念——类
1) 类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象
2) 类就是创建对象的模板
3) 一切皆对象
面向对象的三个特性:继承、封装、多态。
类的构成,(分为3部分):
1) 类的名称:类名
2) 类的属性:一组数据,颜色、大小、高矮,年龄。。
3) 类的方法:允许对进行操作的方法(行为)
定义类
定义一个类,格式如下:
class 类名:
方法列表
创建对象
创建对象的格式为:
对象名=类名()
self的说明
在类的方法中,第一个参数一定是self,表示调用这个方法的对象本身
例,定义一个类和对象
#定义一个类
class Car:
def start(self): #方法的参数一定是self,否则报错
print("汽车启动")
def print_car_info(self):
print("车的名字:%s,颜色为:%s"%(self.name,self.color))
#当创建一个对象时,就是一个模子来制造一个实物
autoHome=Car() #构建一个Car的实例对象,一定在内存中有一块空间存放对象的数据信息,此时可以通过实例对象autoHome来访问属性或者方法,
# autoHome是一个对象,它拥有属性(数据)和方法(函数)
autoHome.name='迈腾' #给对象添加属性,再次出现 autoHome.name='XXXX'表示对属性进行修改
autoHome.color='金色'#'迈腾’,'金色'。。。等数据集中到对象autoHome里面就是对象autoHome的封装
BWM=Car() #构建另外一个对象,与autoHome不同,但同属于一个类
autoHome.print_car_info() #调用对象的方法print_car_info()
autoHome.start() #调用对象的方法start(),这里start()里面的self代表autoHome
BWM.start() # 这里start()里面的self代表对象BWM
魔法方法__init__
1. __init__()方法,在创建一个对象时默认被调用,不需要手动调用
2. __init__(self)中,默认有1个参数名称为self,如果在创建对象时传递了2个实参,那么__init__(self)中除了self作为第一个形参外,还需要2个形参,例__init__(self,x,y)
3. __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前 的对象引用传递进去。
例
'''
class Person:
def __init__(self): #初始化对象的方法,不是构建对象的方法,python解释器自动调用
print("对象初始化")
self.name = 'zs'
self.age = '18'
def work(self):
pass
p1 = Person() #自动调用 __init__()方法
print(p1.name)
'''
class Person():
def __init__(self, name, age, height): #初始化对象的方法,不是构建对象的方法,python解释器自动调用
self.name = name #self.name 指的是对象属性为name,后面的name指的是参数name
self.age = age
self.height = height
def __str__(self): #把对象转换成字符串,还不是地址,必须要有返回值
return "姓名:%s,年龄:%s,身高:%s"%(self.name, self.age, self.height)
def introduce(self):
print("姓名:%s,年龄:%s,身高:%s"%(self.name, self.age, self.height))
p1 = Person('zs', 18, 175) #自动调用 __init__()方法
#p1.introduce()
print(p1)
小结:
1. 在Pyth中方法名如果 是__XXXX__()的,那么就有特殊的功能,因此叫做“魔法”方法
2. 当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从在这个方法中return的数据,必须要有返回值
保护对象的属性
如果有一个对象,当需要对其进行修改属性时,有2种方法
l 对象名.属性名=数据 #直接修改
l 对象名.方法名() #间接修改
为了更好的保存属性的安全,即不能随意修改,一般的处理方式为:
l 将属性定义为私有属性(或称为隐藏属性)
l 添加一个可以调用的方法,供调用
【小结】:
python中没有像C++中private这些关键字来区别公有属性和私有属性,
python以属性命名方式来区分,如果在属性名前面加了2个下划线’__’,则表示该属性为私有属性,否则为公有属性(方法也是一样,方法名前面加2个下划线的话,表示该方法是私有的,否则为公有)
xx | 公有变量 |
|
_x | 单前置下划线 | 私有化属性或方法,不能导入到其他py文件 |
__xx | 双前置下划线 | 避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到) |
__xx__ | 双前后下划线 | 用户名字空间的魔法对象或属性。例如__init__,不要自己发明这样的名字__ |
xx_ | 单后置下划线 | 用于避免与python关键词的冲突 |
class User:
def __init__(self,pw):
if len(pw)>=6:
self.__password = pw #隐藏(私有)属性,不允许外部代码调用和修改
else:
print("密码%s不符合规则"%pw)
def __str__(self):
return "密码%s"%self.__password
def get_password(self):
return self.__password
self.__say_hello() #内部可以调用隐藏方法
def __say_hello(self): #隐藏方法不允许外问题代码调用
print(self.__password)
u1 = User('1231123')
print(u1)
#u1.__say_hello() # 隐藏方法,外部代码无法进行修改
#u1.__password='1234123' # 隐藏属性,外部代码无法进行修改
print(u1.get_password()) # 需要通过对象的方法调用隐藏属性
私有属性:通过name mangling(名字重整)目的就是以防子类意外重写基类的方法或者属性。
_Class_object机制就可以访问private
property用法
为私有属性添加getter和setter方法
使用property升级getter和setter方法
例:money=property(getMoney,setMoney)
使用property取代getter和setter方法(注意:方法名与私有属性名字保持一致)。
方法名前加修饰器@property,该方法变为getter
@property
@money.setter
__del__()方法
创建对象后,python解释器默认用户__init__()方法,
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法,
当内存中构建 一个对象数据时,回调__init__()方法
当内存中销毁(释放)一个对象时,回调__del__()方法
class User:
def __init__(self):
print("******对象初始化*****")
def __del__(self):
print("******对象即将要被销毁******")
u1 = User() # 新建一个对象
u2 = u1 # 将u1 赋值u2 ,共同指向一个对象
del u1 # 删除 u1 与对象的引用关系
print("--"*30)
#del u2 # 删除 u2 与对象的引用关系,对象未被引用(或程序结束时),被回收
print("=="*30)
Ø 当有1个变量保存了对象的引用时,此对象的引用计数就会加1
Ø 当使用DEL删除变量指向的对象时,如果对象的引用计数不是1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用DEL时,变为1,如果 再调用1次DEL,此时会真的把对象进行删除
继承(单继承)
继承,在程序中,继承描述的是事物之间的所属关系,如猫狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承狗。
只继承父类的公有方法
class Animal:
def __init__(self):
print("***动物的初始化***")
self.name='动物'
def eat(self,food):
print("***吃饭***%s" %food)
def sleep(self):
print("***睡觉***")
class Dog(Animal): # 继承父类(Animal)的方法
def __init__(self, name): # 初始化,属性name
self.name = name
def shout(self):
print("***旺旺***")
class Cat(Animal): # 继承父类(Animal)的方法
#def __init__(self): #未定义初始化方法时,调用父类的__init__()方法,
# print("猫初始化了")
def catch(self):
print("----捉老鼠----")
dog = Dog("小白") #传递数据给属性
dog.eat("骨头") # 调用父类的方法
dog.shout()
print(dog.name)
cat = Cat()
print(cat.name)
多继承
class A:
def test(self):
print("A****test()")
class B:
def test(self):
print("B****test()")
class C(A,B):# C继承了父类A和B
def test(self):
print("C****test()")
c = C()
print(C.__mro__) #查看方法优先级
c.test()
输出:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>) 'object'是所有类的父类
C****test()
重写父类方法与调用父类方法
1. 重写父类方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
class Animal:
def __init__(self):
print("***动物的初始化***")
self.color = '黄色'
def eat(self):
print("***Animal类的eat方法" )
def sleep(self):
print("***睡觉***")
class Dog(Animal): # 继承父类(Animal)的方法
def __init__(self, name): # init和父类的init名字一样,所以叫方法的重写
self.name = name
super().__init__() # super().父类方法名称,主调用父类的init方法
def eat(self):
super().eat()
print("**DOG自己的eat方法")
def shout(self):
print("***旺旺***")
dog = Dog("小白")
print(dog.name, dog.color)
dog.eat()
输出:
***动物的初始化***
小白 黄色
***Animal类的eat方法
**DOG自己的eat方法
多态
多态的概念是应用于java和C#这一类强类型语言中,而python崇尚鸭子类型。
所谓多态,定义时的类型和运行时的类型不一样,此时就称为多态。
类属性
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问。
类属性:所属类的,这个类下的所有对象都可以共享这个类属性。相当于JAVA中静态属性,可以被子类继承
class User(object):
name = 'ZhangSan' #类属性,公有
__password = '123456' #类属性,私有(隐藏的)
def __init__(self, sex, username):
self.sex = sex #对象属性
self.username = username #对象属性
u = User("男",'goldbin')
print("u名称:%s"%u.name)#当对象属性与类属性名字一样,如果通过对象来访问,会选择对象属性,通过类访问会选择类属性
u2 = User("女", "adsf")
print("u2名称:%s"%u2.name)
u.name = 'ww' #本质没有修改类的属性,仅仅给该对象定义了一个对象属性name,并赋值为'ww'
print(u.name)
User.name = 'ww' #真正的类属性的修改,通过类的名称来修改
print(User.name)
# 对象属性,对象不一样,属性不一样
# 类属性,所有对象共有同一个类属性
输出:
u名称:ZhangSan
u2名称:ZhangSan
【小结】
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果 通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性
属性叫法 | 变量叫法 | 描述 |
类属性(私有和公有) | 类变量 | 所有对象共享同一份类属性 |
实例属性(私有和公有) | 成员变量 | 每个不同对象,有不一样值的实例属性 |
类方法
类方法是类对象所拥有的方法,需要用修饰器@classmethod来标识 其为类方法,对于类方法,第一个参数必须 是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以‘cls’作为第一个参数的名字,就最好用cls),能够通过实例对象和类对象去访问
class A(object):
name = 'Zhangsan'
def test1(self):
print("A的test1方法")
# 类方法一定要在方法的上面加上一个修饰器(java注解)
# 类方法的参数一定是cls,代表当前的类
@classmethod
def test2(cls): #cls代表当前类
cls.name = 'WangWu'#cls类对象直接修改类属性
print("A的test2方法")
a = A()
a.test1()
a.test2()# 通过实例对象访问类方法
A.test2() # 可以直接用类名调用类方法
print(A.name)
【小结】
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性,也有可能 是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须 通过类对象来引用。
方法类别 | 语法 | 描述 |
类方法 | @classmethod | 第一个形参cls,默认传递 |
静态方法 | @staticmethod | 没有默认传递的形参 |
对象方法(成员方法) | def 方法名 | 第一个形参是self,默认传输 |
静态方法
需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数
class A(object):
name = 'Zhangsan'
@staticmethod # 静态方法,属于类,没有默认传递的参数,可以通过类的对象和类名调用
def test3():
A.name = 'LiSi' # 通过类名称访问类属性
print("A的test3静态方法")
a = A()
a.test3()
A.test3()
print(A.name)
__new__()方法
__new__,对象构造时由python调用的方法,必须有返回值,返回当前类的对象。
Ø __new__与__init__不同时,new方法用于构建对象,init用于初始化对象,默认调用父类object的方法。
Ø __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由python解释器自动提供
Ø __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
Ø __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
Ø 先执行__new__再执行__init__
Ø 我们可以将类比例制造商,__new__方法就是前期的原材料购买环节,__init__方法就是在有原材料的基础上,加工,初始化商品环节
Ø class User(object):
def __init__(self, username, password):
self.username = username
self.password = password
print("对象已经构建好了,由解释器自动以回调,对象初始化")
# new方法是当对象构建 的时候由解释器自动回调的方法。该方法必须返回当前类的对象
def __new__(cls, username, password):
print("User类的对象开始构建")
return object.__new__(cls) # 没有返回构建的对象时,后面代码不执行,调用object类,构建对象
def __str__(self):
return "用户名%s;密码%s"%(self.username,self.password)
u = User('ZhangSan','abc123')
print(u)
输出:
User类的对象开始构建
对象已经构建好了,由解释器自动以回调,对象初始化
用户名ZhangSan;密码abc123
特殊方法名 | 默认的参数 | 功能描述 |
__init__() | self | 初始化对象属性 |
__str__() | self | print对象时,输出 |
__del__() | self | 删除对象 |
__new__() | cls | 创建对象 |
单例模式
单例模式例子:我们日常使用的电脑 上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例,因此回收是单例模式的应用。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这上类称为单例类,单例模式是一种对象创建型模式
使用单例模式的类的特点:
1. 在构造过程中非常复杂,消耗大量内存资源
2. 封装的大量数据,可以当全局变量用
【单实例模式1】
class User(object):
__instance = None
def __init__(self, name):
print("运行__init__()方法")
self.name = name
@classmethod
def get_instance(cls, name):
print("运行get_instance()方法")
if not cls.__instance: # 一开始__instance = None 满足条件,创建对象,
# 后面一直不满足(只运行一次),
# 不创建新的对象实例,后面的name不变
cls.__instance = User(name)
return cls.__instance
u1 = User.get_instance('ZhangSan')
print("u1的name为%s"%u1.name)
u2 = User.get_instance("LiSi")
print("u2的name为%s"%u2.name)
print("最后:u1的name为%s;u2的name为%s" % (u1.name,u2.name))
print(u1 == u2) # 判断表达式,如果返回True,这两具对象是一个对象,并且内存地址相同
print("u1对象的内存地址%s u2对象的内存地址%s"%(id(u1), id(u2)))
输出:
运行get_instance()方法
运行__init__()方法
u1的name为ZhangSan
运行get_instance()方法
u2的name为ZhangSan
最后:u1的name为ZhangSan;u2的name为ZhangSan
True
u1对象的内存地址2529384892176 u2对象的内存地址2529384892176
【单实例模式2】
class User(object):
__instance = None
def __init__(self, name):
print("运行__init__()方法")
self.name = name
def __new__(cls, name):
print("运行__new__()方法")
if not cls.__instance: # 保证 object.__new__(cls)方法只会调用一次
cls.__instance = object.__new__(cls)
return cls.__instance
u1 = User('ZhangSan')
print("u1的name为%s" % u1.name)
u2 = User("LiSi")
print("u2的name为%s" % u2.name)
print("最后:u1的name为%s;u2的name为%s" % (u1.name,u2.name))
print(u1 == u2) # 判断表达式,如果返回True,这两具对象是一个对象,并且内存地址相同
print("u1对象的内存地址%s u2对象的内存地址%s"%(id(u1), id(u2)))
输出:
运行__new__()方法
运行__init__()方法
u1的name为ZhangSan
运行__new__()方法
运行__init__()方法
u2的name为LiSi
最后:u1的name为LiSi;u2的name为LiSi
True
u1对象的内存地址1584795191056 u2对象的内存地址1584795191056
简单工厂模式
工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式,虽然这样做可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量
解决两个对象/两个类之间存在依赖关系的时候的问题。提高系统的维护操作性。
简单工厂模式Simple Factory模式不是独立的设计模式,是Factory Method模式的一种简单的、特殊的实现。也被称为静态工厂模式,通常创建都的创建方法被设计为static方便调用。包含:
1. 静态的工厂类
2. 用全局函数改写工厂类
【例1,静态工厂类】
class Person(object):
def __init__(self, name):
self.name = name
def work(self, axe_type):
print(self.name+"开始工作了") # person完成work,需要一把斧头
# 在原始社会,需要一把石斧
# axe = StoneAxe("花岗岩斧头") # 通过StoneAxe()生产斧头
# axe = SteelAxe("江南十三子斧头") # SteelAxe()生产斧头
axe = Factory.create_axe(axe_type) # 通过 第三方工厂来生产斧头
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧头,开始砍树"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧头砍树"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的钢斧头砍树"%self.name)
class Factory(object):
# 生产斧头,根据用户指标的类型来生产
@staticmethod
def create_axe(type):
if type == "stone":
return StoneAxe("花岗岩斧头")
elif type == "steel":
return SteelAxe("加爵斧头")
else:
print("传入的类型不对")
p = Person("ZhangSan")
p.work("steel")
例2,全局函数
class Person(object):
def __init__(self, name):
self.name = name
def work(self, axe_type):
print(self.name+"开始工作了") # person完成work,需要一把斧头
# 在原始社会,需要一把石斧
# axe = StoneAxe("花岗岩斧头") # 通过StoneAxe()生产斧头
# axe = SteelAxe("江南十三子斧头") # SteelAxe()生产斧头
axe = create_axe(axe_type)# 通过全局函数来生产斧头
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧头,开始砍树"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧头砍树"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的钢斧头砍树"%self.name)
# 全局函数-替代之前的工厂类
def create_axe(type):
if type == "stone":
return StoneAxe("花岗岩斧头")
elif type == "steel":
return SteelAxe("加爵斧头")
else:
print("传入的类型不对")
p = Person("ZhangSan")
p.work("steel")
工厂方法模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态方法,使得它可以被子类继承,对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
抽象的工厂类提供了一个创建对象的方法,也叫作工作方法。
1) 抽象工厂角色(Factory):这是工厂方法模式的核心,它与应用程序无关,是具体工厂色必须 实现的接口或者必须 继承的父类。
2) 具体工厂角色(Stone_Axe_Factory,Steel_Axe_Factory):它含有和具体业务逻辑有关的代码。由应用程序调用以创建不的具体产品时的对象
3) 抽象产品角色(Axe):它是具体产品继承 的父类或者是实现的接口。在python中旬产品一般为父类。
4) 具体产品角色(Stone_Axe,Steel_Axe):具体工厂角色所创建的对象就是此角色 的实例,由一个具体在实现
class Person(object):
def __init__(self, name):
self.name = name
def work(self):
print(self.name+"开始工作了") # person完成work,需要一把斧头
# 在原始社会,需要一把石斧
# axe = StoneAxe("花岗岩斧头") # 通过StoneAxe()生产斧头
# axe = SteelAxe("江南十三子斧头") # SteelAxe()生产斧头
# axe = Steel_Axe_Factory.create_axe() # 通过 第三方工厂来生产斧头
axe = Stone_Axe_Factory.create_axe() # 通过 第三方工厂来生产斧头
axe.cut_stree()
class Axe(object):
def __init__(self,name):
self.name = name
def cut_tree(self):
print("%s斧头,开始砍树"%self.name)
class StoneAxe(Axe):
def cut_stree(self):
print("使用%s做的斧头砍树"%self.name)
class SteelAxe(Axe):
def cut_stree(self):
print("使用%s的钢斧头砍树"%self.name)
# 工厂类
class Factory(object):
# 生产斧头,根据用户指标的类型来生产
def create_axe(self):
pass
class Stone_Axe_Factory(Factory):
def create_axe():
return StoneAxe("花岗岩斧头")
class Steel_Axe_Factory(Factory):
def create_axe():
return SteelAxe("钢铁斧头")
p = Person("ZhangSan")
p.work()
内建属性
内建属性 |
常用专用有属性 | 说明 | 触发方式 |
__init__ | 构造初始化函数 | 创建实例后,赋值时使用,在__new__后 |
__new__ | 生成实例所需属性 | 创建实例时 |
__class__ | 实例所在的类 | 实例__class__ |
__str__ | 实例字符串表示,可读性 | print(类实例),如没实现,使用repr结果 |
__repr__ | 实例字符串表示,准确性 | 类实例,回车 或都print(repr(类实例)) |
__del__ | 析构 | del 删除实例 |
__dict__ | 实例自定义属性 | vars(实例__dict__) |
__doc__ | 类文档,子类不继承 | help(类或实例) |
__getattribute__ | 属性访问拦截器 | 访问实例属性时 |
__bases__ | 类的所有父类 构成元素 | 类名__bases__ |
例__getattribute__属性访问拦截器
属性访问拦截器的坑:
限制任性对象的属性__slots__
注意,__slots__定义的属性公对当前类实例起作用,对继承的子类是不起作用的
类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
一般callable对象都是函数,但也有例外。只要某个对象重写了__call__()方法,那么这个对象就是callable的
例:类当函数用
例2类装饰器