面向对象上
面向对象简介
- python是一门面向对象的编程语言。
- 所谓面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的。
- 面向过程
– 面向过程指将我们的程序分解为一个一个步骤,通过对每个步骤的抽象来完成程序。
– 这种编写方式往往只适同于一个功能,如果要实现别的功能,往往复用性比较低。
– 这种编写方式符合人类的思维,编写起来比较容易。
-1. 妈妈穿衣服穿鞋出门
-2. 妈妈骑上电动车
-3. 妈妈到超市门口放好电动车
-4. 妈妈买西瓜
-5. 妈妈结账
-6. 妈妈 骑电动车回家
-7. 到家孩子吃西瓜 - 面向对象 编程 语言,关注的是对象,而不注重过程,对于对象一切皆为 对象。
- 以上方式可以用孩子妈妈 给孩子买瓜来解决。
- 面向对象的编程思想,将所有的功能保存到对应的对象中,要使某个功能,直接找到对应的对象即可。
- 这种编码方式比较易于阅读,并且易于维护,容易复用,但是编写过程中不太符合常规的思维,编写相对麻烦。
类
- 我们目前学习的都是python的内置对象,但是内置对象并不都能满足我们的需求,所以在开发中我们经常要自定义一些对象。
- 类简单理解他就是相当于一个图纸,在程序汇总我们需要根据类来创建对象。
- 类就是对象的图纸
- 我们也称为对象是类的实例(instance)
- 如果多个对象是通过一个类创建的,我们称为这些对象是一类对象。
1. #语法
2. class 类名([父类]):
3. pass
- 类也是一个对象,类就是用来创建对象的对象。
- 可以像对象中添加变量,对象中的变量称之为属性: 对象.属性名 = 属性值
类的定义
- 类和对象都是对现实生活中事物的抽象。
- 事物包含两部分
– 1. 数据(属性)
– 2. 行为(方法) - 调用方法 对象.方法名()
- 方便调用和函数调用的区别:如果是函数调用,调用时有几个形参,就会传递几个实参。如果是方法调用,默认一个参数,所以方法中至少得有一个形参。
# 在python中,你想使用一个东西,你必须先创建一个这样的对象
# 自定义类 如:图纸 通过图纸创造出来的汽车 汽车就是通过图纸创建出来的实例对象
# 定义类的语法
# class 类名([父类]):
# pass
class MyClass(): #定义类,给它命名为MyClass 类相当于图纸 类对象 只是用于存储的功能
pass
mc = MyClass() #创建实例对象(类的实例化)实例对象 实现效果的功能
#类对象 (存储属性、功能) 实例对象(实现效果的功能)
mc1 = MyClass()
print(id(mc),id(mc1))
#类可以创建多个实例,每个实例各不相同,这一些实例我们称呼为一类实例
#isinstance() 用来检测这个实例是否是这个类创建出来的
print(isinstance(mc,MyClass))
- 在类代码块中,我们可以定义变量和函数。
– 变量会成为该类实例的公共属性,所有的该实例都可以通过对象. 属性名的形式访问。
– 函数会成为该类实例的公共方法,所有该类实例都是可以通过 对象.方法名的形式访问。
class Person(): #定义一个类
name = '邓紫棋' #属性
def speak(self): #方法 类中的函数不叫做函数了,叫做方法
print('你好')
p1 = Person() #类的实例化
print(p1.name) #实列.属性 就可以获取到这个属性值
p1.speak() #实例.方法
p1.name = '刘亦菲' #更改类的属性的值
print(p1.name)
p2 = Person()
print(p2.name)
参数self
###属性和方法
- 类中定义的属性和方法都是公共的,任何该类实例都是可以访问。
- 属性和方法的查找流程
– 当我们调用一个对象的属性时,解析器会现在当前的对象中寻找是否还有该属性,如果有,则直接返回类对象的属性值。如果没有就报错。 - 类对象和实例对象中都可以保存属性(方法)
– 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中。
– 如果这个属性(方法)是某个实例独有的。则应该保存到实例对象中。
– 一边情况下,属性保存到实例对象中,而方法需要保存到类对象中。
seif
- seif在定义时需要定义,但是在调用时会自动传入。
- seif的名字并不是规定死的,但是最好按照约定是同seif。
- seif总是指调用时的类的实例。
class Presion():
#name = '刘亦菲'
#哪个对象调用类的speak方法,那个s就是这个对象,类里通常使用self
def speak(s):
print('你好,我是%s'%s.name)
def run(self): #self就等于任何调用这个方法的对象本身
pass
p1 = Presion()
p1.name = '周慧敏'
p1.speak()
p2 = Presion()
p2.name = '刘亦菲'
p2.speak()
面向对象中
特殊方法
- 在类中可以定义一些特殊方法也称为魔术方法。
- 特殊方法都是形如_ xxx _()这种形式。
- 特殊方法不需要我们调用,特殊方法会在特定时候自动调用。
class Person():
def __init__(self,name,age): #实例创建的时候,init方法就会被调用
self.name=name
self.age=age
def speak(self):
print('大家好,我是{}'.format(self.name))
p1=Person('刘亦菲',18)
p1.speak()
p2=Person('周慧敏',18)
p2.speak()
封装
- 封装的概念:就是为了数据的安全,形成了一个默认的规则,告诉你的协同开发者,这个属性很重要,不要随意修改。
class Car():
def __init__(self,name,color):
self.name=name
self.color=color
def run(self):
print('启程开始跑起来了')
def dididi(self):
print('汽车鸣笛了')
car=Car('法拉利','红色')
car.name='中华田园犬'
print(car.name) #修改了前面的类属性
car.run()
car.dididi()
----运行结果-----
中华田园犬
启程开始跑起来了
汽车鸣笛了
-
出现封装的原因:我们需要一种方法来增强数据的安全性
– 属性不能随意更改
– 属性不能改为任意值 -
封装是面向对象的三大特性之一。
-
封装是指隐藏对象中一些不希望被外部访问到的属性或方法。
-
我们可以提供一个getter()和setter()方法是可以访问到外部属性
– getter()获取对象中的属性。
– setter()用来设置对象的指定属性。 -
使用封装,确实增加了类的定义的复杂程度,但是它确实保了数据的安全。
– 隐藏属性名,使调用者无法随意的修改对象中的属性。
– 增加了getter()和setter()方法,很好控制属性是否是只读的。
– 使用setter()设置属性,可以在做一个数据的验证。
– 使用getter()方法获取属性,使用setter()方法设置属性可以在读取属性和修改属性的同时做一些其他的处理。 -
可以为对象的属性使用双下画线开头_ _xxx。 双下划线开头的属性,是对像的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问。
-
其实隐藏属性只不过是python自动为属性改了一个名字 —> _类名 _ _属性名 例如: _ _name -> _ person _ _name
-
这种方式实际上依然可以在外部访问,所以这种方式我们一般不用。一般我们会将一些私有属性以_开头。
-
一般情况下,使用_开头的属性都是私有属性,没有特殊情况下不要修改私有属性。
# 封装:私有属性
class Car():
def __init__(self,name,color):
self.name=name # 提醒不要去修改属性,协商原则,但依然是可以修改的
self.color=color
def run(self):
print('启程开始跑起来了')
def dididi(self):
print('汽车鸣笛了')
car=Car('法拉利','红色')
car.name='中华田园犬'
print(car.name) #修改了前面的类属性
car.run()
car.dididi()
----运行结果-----
中华田园犬
启程开始跑起来了
汽车鸣笛了
# 封装:隐藏属性
class Car():
def __init__(self,name,color):
self.__name=name # 隐藏属性不能更改
self.color=color
def run(self):
print('启程开始跑起来了')
def dididi(self):
print('汽车鸣笛了')
car=Car('法拉利','红色')
# car.__name='中华田园犬'
print(car.__name) # 隐藏属性 不可读的属性
car.run()
car.dididi()
----运行结果-----
AttributeError: 'Car' object has no attribute '__name'
class Car():
def __init__(self,name,color):
self._name=name # 隐藏属性不可读的属性
self.color=color
# getter方法 提供给你访问这个属性的方法
def get_name(self):
return self._name
# setter方法 提供给你修改这个属性的方法
def set_name(self,name):
self._name=name
def run(self):
print('启程开始跑起来了')
def dididi(self):
print('汽车鸣笛了')
car=Car('法拉利','红色')
# car.__name='中华田园犬'
car.run()
car.dididi()
car.set_name('玛莎拉蒂')
print(car.get_name())
----运行结果-----
启程开始跑起来了
汽车鸣笛了
玛莎拉蒂
property装饰器
- 我们可以使用@property装饰器来创建只读属性,@property装饰器将方法转换为相同名称的只读属性,可以以所定义的属性配合使用,这样可以防止属性被修改。
class Car():
def __init__(self,name,color):
self._name=name # 隐藏属性不可读的属性
self.color=color
# getter方法 提供给你访问这个属性的方法
@property
def get_name(self):
return self._name
# setter方法 提供给你修改这个属性的方法
@property
def set_name(self,name):
self._name=name
def run(self):
print('启程开始跑起来了')
def dididi(self):
print('汽车鸣笛了')
car=Car('法拉利','红色')
# car.__name='中华田园犬'
car.run()
car.dididi()
car.name='玛莎拉蒂'
print(car.name)
面向对象下
继承简介
- 继承是面向对象三大特性之一。
- 通过继承我们可以使一个类获取其他类的中的属性和方法。
- 在定义时,可以在类名后面的括号中指定当前类的父类(超类、基类)。
- 继承提高了类的复用性。让类与类之间产生了关系,才有了多态的特性。
方法重写
- 如果在子类中有和父类同名的方法,则通过子类实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点我们称之为方法的重写(覆盖)
- 当我们调用一个对象的方法时:
– 会优先去当前对象中寻找是否具有该方法,如果有则直接调用。
– 如果没有,则去当前对象的父类寻找,如果父类有则调用父类中的方法。
– 如果没有,则去父类中的父类寻找,以此类推,直到找到object,如果依然没有就会报错了。
super( )
- super()可以获取当前类的父类。
- 并且通过super()返回对象调用父类方法时,不需要传递seif
多重继承
- 在python中是支持多种继承的,也就是我们可以为一个类同时制定多个父类。
- 可以在类名的()后边添加多个类,来实现多重继承。
- 多重继承,会使子类同时拥有多个父类,并且会获取所有父类中的方法。
- 在开发中没有特殊情况,应该避免使用多重继承。因为多重继承会让我们的代码更加复杂。
- 如果父类中有同名的方法,则会先在第一个父类中寻找,然后第二个,第三个。。。。前面会覆盖后面的。
多态
-
多态是面向对象的三大特性之一。从字面理解就是多种形态。
-
一个对象可以,以不同的形态去呈现。
-
面向对象三大特性
– 封装 确保对象数据的安全。
– 继承 保证了对象的扩展性。
– 多态 保证了程序的灵活性。 -
python中多态的特点
– 只关心对象实例方法是否同名,不关心对象所属的类型。
– 对象所属的类之间,继承关系可有可无。
– 多态的好处可以增加代码外部调用灵活度,让代码更加通用,兼容性比较强。
– 多态是调用方法的技巧,不会影响到类的内部设计。
属性和方法
属性
- 类属性,直接在类中定义的属性是类属性。
- 类属性可以通过类或类的实例访问到。但是类属性只能通过类类对象来修改,无法通过实例对象来修改。
- 实例属性,通过实例对象添加的属性属于实例属性。
- 实例属性只能通过实例对象来访问和修改,类对象无法访问修改。
方法
- 在类中定义,以seif’为第一个参数的方法都是实例方法。
- 实例方法在调用时,python会将调用对象以seif传入。
- 实例方法可以通过类实例和类去调用。
- 当通过实例调用时,会自动将当前调用对象作为seif传入
- 当通过类调用时,不会自动传递seif,我们必须手动传递seif
- 类方法,在类的内容以@classmethod 来修饰的方法属性类方法。
- 类方法第一个参数是cls 也会自动被传递。cls就是当前的类对象。
- 类方法和实例方法的区别,实例方法的第一个参数是seif,类方法的第一个参数是cls
- 类方法可以通过类去调用,也可以通过实例调用。
静态方法
- 在类中用@staticcmethod 来修饰的方法属于静态方法。
- 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例来调用。
- 静态方法,基本上是是和一个和类当前无关的方法,它只是保存到当前类中的函数。
- 静态方法一般都是些工具类方法,和当前类无关。
单例模式
_ _new _ _() 方法
_ _ new _ _() 方法用于创建与返回一个对象。在类准备将自身实例化时调用。
练习
以以下代码打印出的顺序?
A. _ _ init _ _ , _ _ new _ _
B. _ _ init _ _
C. _ _ new _ _
D. _ _ new _ _ init _ _
class Demo(object):
def __init__(seif):
print('_ _ init _ _')
def __new__(cls, *arge, **kwargs):
print('_ _new_ _')
d = Demo()
注意
- new() 方法用于创建对象。
- init() 方法在对象创建的时候,自动调用。
- 但是此处重写了父类的__new__() 方法,覆盖了父类 new() 创建对象的功能,所以对象并没有成功。所以仅执行__new__()方法内部代码。
对象创建执行顺序
- 通过__new__()方法创建对象
- 并将对象返回,传给__init__()
练习
在自定以类中实现创建对象
思路
- 重写父类__new__()方法
- 并且在该方法内部,调用父类__new__()方法
class Demo(object):
def __init__(self):
print('__init__')
def __new__(cls, *arge, **kwargs):
print('__new__')
return super().__new__(cls)
d = Demo()
注意
- 在创建对象时,一定要将对象返回,才会触发__init__()方法
- init()方法当中的seif,实际上就是__new__返回的实例,也就是该对象。
init__与__new()区别
- init 实例方法,__new__静态方法
- init 在对象创建后自动调用,new 创建对象的方法。
单例模式
– 单例模式介绍:单例模式是一种常用的软件设计模式。也就是说该类只包含一个实例。
– 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便实例对象个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
– 通常应用在一些资源管理器中,比如日志记录等。
单例模式实现
思路
- 当对象不存在时,创建对象。
- 当对象存在时,永远返回当前已创建的对象。
class single(object):
__isinstance = None
def __new__(cls, *arge, **kwargs):
if cls.__isinstance is None:
cls.isinstance = super().__new__(cls)
return cls.__isinstance
else:
return cls.__isinstance
a = single()
b = single()
print(id(a))
print(id(b))