面向对象
对象的简介
- Python是一个面向对象的编程语言。
- 对象就是内存中用来存储指定数据的一块区域。
- 对象的结构:
每个对象当中保存了3种数据:
id(标识) id是由解析器生成的,在cpython中id就是对象的内存地址
type(类型) 用来标识当前对象所属的类型 类型就决定了对象有什么功能
value(值) 就是对象中存储的具体数据 - 对象分为两大类:
可变对象 不可变对象
可变对象值可以改变 不可变对象值不可以改变 - 面向过程:
面向过程将我们的程序分解为一个一个的步骤,通过对每一个步骤的抽象完成程序
这种编写方式往往只适用于一个功能,如果要实现别的功能,往往复用性比较低
这种编写方式符合人类的思维,编写起来也比较容易。 - 面向对象:
面向对象的编程语言,关注的是对象,而不注重过程,对于面向对象一切皆对象。
面向对象的编程思想,将所有的功能统一保存到对应的对象中,要使用某个功能,直接找到对应的对象即可。
编写方式比较易于阅读,易于维护,容易复用,但是编写过程中不太符合常规的思维,编写相对麻烦。
类(class)
之前学习的是python的内置对象例如int() str() list() dict()
a = int(123)# 创建一个int类的实例
b = str('hello')# 创建了一个str类的实例
print(a,type(a),' ',b,type(b))
123 <class 'int'> hello <class 'str'>
内置对象并不可以满足实际开发的需求,所以我们需要自定义一些对象
类:简单理解相当于一个图纸,在程序中我们需要根据类创建对象,我们也称对象是类的实例
如果多个对象是通过一个类创建出来的,我们称这些对象是一类对象
我们使用class这个关键字来定义类
- 语法:
class 类名([父类]):
代码块
class MyClass():
pass
print(MyClass)
mc1 = MyClass()# mc是通过MyClass来进行创建的,mc就是MyClass的实例
mc2 = MyClass()
mc3 = MyClass()
mc4 = MyClass()
print(mc1,type(mc1))
# isinstance()用来检查一个对象是否是另一个类的实例,如果是返回一个True,否则返回False
r = isinstance(mc1,MyClass)
print(r)
isinstance()用来检查一个对象是否是另一个类的实例,有返回值,如果是返回一个True,否则返回False
<class '__main__.MyClass'>
<__main__.MyClass object at 0x0000014509C0E788> <class '__main__.MyClass'>
True
类也是一个对象,是一个用来创造对象的对象,在对象中通常由value id type,那么类有没有这些呢?
class MyClass():
pass
print(MyClass,' ',id(MyClass),' ',type(MyClass))
<class '__main__.MyClass'> 1387507970744 <class 'type'>
所以类是type类型的对象,定义类实际上就是定义了一个type类型的对象
class MyClass():
pass
mc = MyClass()
mc2 = MyClass()
mc.name = '葫芦娃'
#print(MyClass,' ',id(MyClass),' ',type(MyClass))
mc2.name = '钢铁侠'
print(mc.name)
print(mc2.name)
葫芦娃
钢铁侠
类的定义:
类和对象都是显示生活中的抽象
实际上所有的事物由两部分组成:
数据(属性)
行为(方法)
class Person:
# 在类中的代码块我们可以定义变量和还能输,在类中定义的变量将会成为所有实例的公共属性
a = 123
b = 456
name = '葫芦娃'
# 方法调用和函数调用的区别:
# 如果是函数调用:有几个形参,就会传递几个实参
# 如果是方法调用:默认要指定一个形参,所以方法中至少要有一个形参
def speak(w):
print('大家好')
# 类中的函数我们称之为方法或者行为
# 创建Person的实例
p1 = Person()
p2 = Person()
print(p1.a,p1.name,p2.b)
# 调用方法:
# 语法:对象.方法名
print(p1.speak)
p1.speak()
123 葫芦娃 456
<bound method Person.speak of <__main__.Person object at 0x000001D9B8F83C48>>
大家好
属性和方法
实例对象可以访问类对象中的属性和方法
- 属性和方法的查找流程
当我们调用一个对象的属性时,解析器先会在当前对象中寻找是否有该属性,如果有返回当前对象的属性值,如果没有则去当前对象的类对象中去寻找,如果有就返回,如果类的对象中也没有则进行报错
class Person:
name = '葫芦娃'
def speak(w):
print('大家好')
p1 = Person()
p1.name = '钢铁侠'
print(p1.name)
p2 = Person()
类对象和实例对象都可以保存属性和方法
如果这个属性或者方法时所有实例共享的,则应该保存到类对象中
如果这个属性或者方法是某一个实例对象独有的,则应该保存到实例对象中
一般情况下,属性保存到实例对象中,而方法一般保存到类对象中
钢铁侠
self参数
class Person:
name = '葫芦娃'
def speak(self):
# 在类总定义方法会默认传递一个参数,通过现象我们发现如果是p1调用则w就是p1对象,如果是p2调用w就是p2对象
# 我们都会称这个参数为self
#print(w)
print('你好我是%s'% self.name)
p1 = Person()
p2 = Person()
p1.name = '钢铁侠'
p2.name = '绿巨人'
p1.speak()
p2.speak()
#print(p1,p2)
你好我是钢铁侠
你好我是绿巨人
特殊方法
class Person:
#print('类中的代码:')
#name = '葫芦娃'
# 在类中可以定义一个特殊方法,这些特殊方法形如__开头__,特殊方法不需要我们去调用,特殊的方法会在特殊的时候自动调用
# 特殊方法
def __init__(self,name):
#print('hello')
#print(self)# 打印的对象
self.name = name
def speak(self):
print('大家好,我是%s'%self.name)
#print('类中的代码:')
p1 = Person('绿巨人')# 通过类创建几次对象__init__特殊方法就会执行几次,类中的代码先进行执行执行一次,在去执行__init__方法
p2 = Person('钢铁侠')
p3 = Person('黑寡妇')
#p1 = Person()
#p1 = Person()
#p1 = Person()
p1.speak()
p2.speak()
p3.speak()
#p1.__init__()
## 手动添加属性的值
#p1.name = '钢铁侠'
#p2 = Person()
#p2.name = '绿巨人'
#p3 = Person()
#p3.name = '黑寡妇'
#p1.speak()
#p2.speak()
#p3.speak()
# 目前来说name对我们的程序是必须的,并且name又是不同的,手动添加name属性容易遗忘
'''
类的基本结构:
class 类名([父类]):
公共属性.....
# 对象的初始化方法
def __init__(self,.....):
pass
# 其他的方法
def method1(self):
pass
def method2(self):
pass
def method3(self):
pass
.....
'''
大家好,我是绿巨人
大家好,我是钢铁侠
大家好,我是黑寡妇
封装
# 定义一个车类
'''
属性:name color
方法:run() laba()
'''
class Car():
def __init__(self,name,color):
self.name = name
self.color = color
def run(self):
print('汽车开始跑了')
def laba(self):
print('%s滴滴滴滴滴滴滴'%self.name)
c = Car('大奔','白色')
# print(c)# <__main__.Car object at 0x0000023129A47FC8>
print(c.name,c.color)
c.run()
c.name = '法拉利'
c.laba()
# 我们需要一种方式来增强数据的安全性
# 1.属性不可以随意的修改(我让你改,你才可以改)
# 2.属性不可以改为任意的值
大奔 白色
汽车开始跑了
法拉利滴滴滴滴滴滴滴
面向对象的三大特性之一
封装就是指隐藏对象汇总的一些不希望被外部访问到的属性或者方法
class Dog:
def __init__(self,name):
self.name = name
d = Dog('大狼狗')
print(d.name)
d.name = '大豺狗'
print(d.name)
程序中的数据不可以保证安全
大狼狗
大豺狗
要想保证数据的安全性要将数据藏一部分和露一部分
接下来就研究怎么藏起来那一部分
解决方法:将对象的属性名称修改为一个外部不知道的名字
class Dog:
def __init__(self,name):
self.hidden_name = name
def speak(self):
print('大家好,我是%s'%self.hidden_name)
d = Dog('大狼狗')
d.speak()
大家好,我是大狼狗
那么我们怎么将数据去露一部分呢?
如果要修改属性,我们要提供一个getter和setter方法使外部可以访问到属性并且修改
使用封装,确实增加了类的定义的复杂的程度,但是也保障了数据的安全性
1.隐藏属性名,使调用者无法随意修改对象中的属性
2.增加了getter()和setter()方法,可以很好的控制属性是否是只读的
3.使用setter()设置属性,可以增加数据的验证,确保数据是正确的
4.使用getter()方法获取属性,使用setter()方法设置属性可以在读取属性和修改属性的同时做一些其他的处理
class Dog:
def __init__(self,name,age):
self.hidden_name = name
self.hidden_age = age
def speak(self):
print('大家好,我是%s'%self.hidden_name)
# get_name()用来获取对象的属性值
def get_name(self):
return self.hidden_name
# setter_name()用来修改对象的属性值
def setter_name(self,name):
self.hidden_name = name
def get_age(self):
print('用户读取的属性')
return self.hidden_age
def set_age(self,age):
print('用户修改的属性')
if age > 0:
self.hidden_age = age
d = Dog('大狼狗',5)
d.set_age(8)
print(d.get_age())
#print(d.get_name())
#d.setter_name('大黑背')
#print(d.get_name())
#d.speak()
用户修改的属性
用户读取的属性
8
class Person:
# 可以为对象的属性使用双下划线的方式类进行封装__xxx,双下划线是对象的隐藏属性,隐藏属性只能再类的内部访问,无法通过对象访问
# 隐藏属性只不过是python自动给属性起了另外的一个名字
# 这个名字叫做__类名__属性名 __name---->__Person__name
# 使用双下划线的方式的属性,实际上依然可以在外部访问的,所以这种方式一般不用
# 一般对属性进行封装,都是以单下划线开头的方式
def __init__(self,name):
# self.hidden_name = name
# self.__name = name
self._name = name
def get_name(self):
return self._name
def set_name(self):
self._name = Name
p = Person('葫芦娃')
##p.set_name('钢铁侠')
#print(p.get_name())
##print(p.__name)
#print(p.__Person__name)
#p._Person__name = '蝙蝠侠'
print(p._name)
葫芦娃
@property装饰器
@property来创建的只读属性,这个装饰器会将方法转换为相同名称的只读属性
class Person:
def __init__(self,name):
self._name = name
# getter方法
@property
def name(self):
print('get方法执行了')
return self._name
p = Person('葫芦娃')
# print(p.name())# 'str' object is not callable
print(p.name)
get方法执行了
葫芦娃
import requests
r = requests.get('xxxxxxxxxxxxxxxxxxxx')
content = r.text
class Person:
def __init__(self,name):
self._name = name
# getter方法
@property
def get_name(self):
print('get方法执行了')
return self._name
#setter方法 @属性名.setter
@name.setter
def set_name(self,name):
self._name = name
继承
继承的简介
class Doctor():
name = ''
age = ''
def study(self):
print('治病求人')
class Soldier():
name = ''
age = ''
def study(self):
print('保卫祖国')
这些类中有一些共性的内容,但是这样的编写会有一些繁琐,我们可以将这些类中的有共性的一些内容单独放到一个类中去,之后再让这个类与其他的类发生一系列的关系,使其他的类也有共性的内容,发生关系的过程就是继承,这便是对继承的理解。
# 继承的简介
# 1.让类与类之间产生了关系,有了这层关系才有后面的多态
# 2.提高了代码的复用性
class Person():
name = ''
age = ''
class Doctor():
name = ''
age = ''
def study(self):
print('治病求人')
d = Doctor()
class Soldier():
name = ''
age = ''
def study(self):
print('保卫祖国')
继承的引入
class Animal:
# 这是一个动物类
def sleep(self):
print('睡觉')
def run(self):
print('跑')
a = Animal()
a.run()
# 定义一个狗类
# 思路一:
# 1.直接再动物类上面修改,添加上狗所独有的功能
# 2.这样修改起来会比较麻烦,会违反OCP原则
# 思路二:
# 创建一个新的狗类
# 创建一个新的类比较麻烦,会出现大量的复制粘贴
class Dog:
# 这是一个狗类
def sleep(self):
print('狗睡觉')
def run(self):
print('狗跑')
def home(self):
print('狗看家')
d = Dog()
d.run()
# 思路三
# 直接从animal类中继承它的属性和方法
# 再定义类的时候,可以再类名后的括号中指定当前类的父类(父类、超类)
class Dog(Animal):
def speak(self):
print('汪汪汪')
p = Dog()
d.run()
#d.speak()
# 继承是面向对象的三大特性之一
r = isinstance(d,Dog)
print(r)
r = isinstance(d,Animal)
print(r)
# 在创建类的时候,如果省略了父类,则默认父类为object
# object是所有类的父类
# issubclass()检查一个类是不是另一个类的子类,如果这个类是另一个类的子类我们就返回True
print(issubclass(Dog,Animal))
print(issubclass(Dog,object))
print(issubclass(Animal,object))
print(issubclass(int,object))
跑
狗跑
狗跑
False
False
True
True
True
True
继承的类和父类里面如果出现了相同名称的方法,先会在自己里面找,如果没有找到就到它的父类中去找,如果父类中还是没有找到就进行报错
方法的重写
如果子类中有和父类中重名的方法,则通过子类实例去调用该方法时,会调用子类中的方法而不是父类中的方法,这个称为是方法的重写(覆盖)
class Animal(object):
def run(self):
print('跑')
def sleep(self):
print('睡觉')
class Dog(Animal):
def speak(self):
print('汪汪汪汪')
def run(self):
print('狗跑')
d = Dog()
d.run()
狗跑
class A(object):
def test(self):
print('a')
class B(A):
def test(self):
print('b')
class C(B):
pass
c = C()
c.test()
b
当我们调用一个对象的方法的时候:
会优先去当前对象中寻找是否具有该方法,如果有则直接调用
如果没有,就去当前对象的父类中去寻找,如果父类有则直接调用父类中的方法
如果没有,则去父类中的父类寻找,以此类推,直到找到object,如果依然没有找到就报错
super方法
class Animal:
def __init__(self,name):
self._name = name
def sleep(self):
print('动物睡觉')
def run(self):
print('动物会跑')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
# 父类中所有的方法都会被继承包括特殊方法 狗--->犬科
class Dog(Animal):
def __init__(self,name,age):
#self._name = name
# 希望可以直接调用父类中的__init__()
#Animal.__init__(self,name)
# super()可以用来获取当前类的父类,通过super()不需要传递self
super().__init__(name)
self.age = age
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
def home(self):
print('狗会看家')
def speak(self):
print('汪汪汪')
def run(self):
print('狗跑')
d = Dog('大狼狗',12)
d.name = '二哈'
print(d.name,d.age)
二哈 12
多重继承
在python中是支持多继承的,也就是我们可以为一个类指定多个父类
如果多个父类中出现同名的方法,则先会在第一个父类中寻找,然后在第二个父类中寻找,以此类推
class A(object):
def test1(self):
print('a')
class B(object):
def test1(self):
print('b中test1的方法')
def test2(self):
print('b')
class C(B,A):
pass
# 如果多个父类中出现同名的方法,则先会在第一个父类中寻找,然后在第二个父类中寻找,以此类推
# 类名.__bases__这个属性可以获取当前类的所有父类,以元组的形式来进行返回
print(C.__bases__)
c = C()
c.test1()
c.test2()
(<class '__main__.B'>, <class '__main__.A'>)
b中test1的方法
b
多态
多态是面向对象的三大特性之一
一个对象可以有不同的形态去呈现
class A(object):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B(object):
def __init__(self,name):
self._name = name
def __len__(self):
return 1000
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
a = A('葫芦娃')
b = B('钢铁侠')
class C:
pass
c = C()
print(len(b))
# 定义一个函数
# speak2()这个函数就违反了多态
def speak(obj):
print('好好复习%s'%obj.name)
def speak2(obj):
# 类型检查,检查一个对象是否是另一个对象的实例
if isinstance(obj,A):
print('好好复习%s'%obj.name)
speak(a)
speak(b)
#speak2(b)
#speak(c)
# len()检查一个序列的长度,通过对len()函数的说明,len()函数的用法就是多态类型的一种表现
lst = [1,2,3,4,5,6,7,8,9]# list
s = 'python'
print(len(lst))# str
print(s)
# len()函数之所以可以获取长度,是因为对象中有一个特殊的方法:__len__()
1000
好好复习葫芦娃
好好复习钢铁侠
9
python
属性和方法
# 属性和方法
class A(object):
# 类属性 直接在类中定义的属性
count = 0
def __init__(self):
# 实例属性只可以通过实例对象来进行访问,类对象无法进行访问和修改
self.name = '葫芦娃'
# 实例方法都是在类中直接定义的,在类中定义以self为第一个参数的方法都是实例方法
# 当通过实例对象调用时,会自动传递当前对象作为self传入
# 当通过类去调用时,不会自动传递self
def text(self):
print('这是text方法')
# 类方法的创建
# 在类的内部使用@classmethod修饰的方法属于类方法
# 类方法的第一个参数我们习惯写成cls,也会自动传递,cls相当于当前的类对象
@classmethod
def test2(cls):
print('这是test2方法')
print(cls.count)
# 静态方法
# 在类中用@staticmethod来修饰的方法我们称之为静态方法
@staticmethod
def text3():
print('这个是text3方法')
# name是实例属性
# 在类中创建的属性可以通过类和该类的实例进行访问
# 实例属性:通过实例的对象来进行添加的属性就是实例属性
print('实例对象属性修改前:')
print(A.count)
a = A()
print(a.count)
print('实例对象属性修改后:')
a.count = 10
print(A.count)
print(a.count)
print('类属性修改后:')
A.count = 20
print(A.count)
print(a.count)
# 类中的属性只可以通过类对象来进行修改,无法通过实例对象进行修改
print('a',a.name)
#print('A',A.name)
# 实例方法可以通过类对象调用,也可以通过实例对象调用
a.text()# 等价于A.text(a)
A.text(a)
# 类方法可以通过类对象调用,也可以通过实例对象调用
A.test2()# 等价于a.test2()
a.test2()
# 静态方法不需要指定任何的默认参数,静态方法可以被类对象和实例对象调用
# 静态方法跟类的本身无关,就是一个功能函数
A.text3()
a.text3()
实例对象属性修改前:
0
0
实例对象属性修改后:
0
10
类属性修改后:
20
10
a 葫芦娃
这是text方法
这是text方法
这是test2方法
20
这是test2方法
20
这个是text3方法
这个是text3方法
总结
封装:确保了对象中的数据安全
继承:保证了对象的可拓展性
多态:保证了对象的灵活性