目录
一.面向对象编程
二.类和对象的介绍
三.私有属性与私有方法
四.成员
五.字段
六.方法
七.属性
八.面对对象的三大特征
九.封装
十.继承
十一.多态
十二.魔法方法
十三.单例模式
十四.反射
一.面向对象编程
1.面向对象编程介绍
面向对象编程:Object Oriented Programming,简称OOP,是一种程序设计思想。需要注意的是,与之对应的是面向过程的编程思想。实际上,能够使用面向对象编程思想实现的程序,也能够通过面向过程完成。只是看那种思想更适合当前的程序开发。
2.面向过程和面向对象的区别
面向过程:根据业务逻辑从上到下写代码。
面向对象:将数据与函数绑定到一起,进行封装。减少重复代码的重写过程。
3.面向对象概念及术语
(1)类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称为类的实例。
(2)对象:也称实例。通过类定义的初始化方法,赋予具体的值,成为一个"有血有肉的实体"。
(3)实例化:创建类的实例的过程或操作。
(4)实例变量:定义在实例中的变量,只作用于当前实例。
(5)类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。
(6)数据成员:类变量,实例变量,方法,类方法,静态方法和属性等的统称。
(7)方法:类中定义的函数。
(8)静态方法:不需要实例化就可以由类执行的方法。
(9)类方法:类方法是将类本身作为对象进行操作的方法。
(10)方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类方法进行改写,这个过程也称为:override。
(11)封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制。
(12)继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
(13)多态:根据对象类型的不同以不同的方式进行处理。
二.类和对象的介绍
1.类
类是抽象的概念,仅仅是模板。用来描述具有相同属性和方法的对象的集合。比如:’人‘是一个类,而其中的某一个人比如说张三,则是属于人这个类中的一个对象。
2.对象
某一个具体事物的存在,在现实中可以是看得见摸得着的。比如张三就是一个对象。
3.类和对象的关系
那么实际上,我们可以进行对象归类。
比如:分析班级同学公有的特征归类。
那么,我们可以将学生共有的特征进行归类。而建立一个学生类。如下:
4.类的构成
类由3个部分构成:
(1)类的名称:类名
(2)类的属性:一组数据
(3)类的方法:允许对类进行操作的方法
注意:类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。
5.类的定义
python使用关键字class来定义类。其基本结构如下:
class 类名: #最好别用中文
pass
6.创建对象
python中,可以根据已经定义的类去创建一个个对象,创建格式如下:
对象名 = 类名()
比如说现在要创建一个学生类,创建一个对象为张三,在类中定义方法输出,张三学习python,代码如下:
7.总结:
# 定义
class 类名:
def 方法名(self,参数): # 类中函数:称为方法
pass
# 执行
s = 类名() # 创建对象(实例) 整个过程就是实例化
s.方法名(参数) # 调用类中方法
8.self参数
在类中定义方法时,会发现系统帮我们自动创建self参数,并且在调用对象的该方法时,也无需传入self参数。
实际上,我们需要明确self的两个概念:
(1)self是形参
(2)self就是对象本身
练习:定义学生类,创建对象为李四,在类中定义方法:打印李四信息
那么实际上,在这个过程当中,对象将自身信息在类的外部赋值,封装到了类的内部。如下图:
但当我在想创建一个对象时,并且打印出对象信息时,就要重新定义:
class Student(object):
def info(self):
print(self.name,self.age)
ls = Student()
ls.name = "李四"
ls.age = 18
ls.info()
ww = Student()
ww.name = "王五"
ww.age = 20
ww.info()
但是这样做仍然有缺陷,大家会发现,用户的信息暴露在类的外部。
9.__init__()方法
__init__()方法称为初始化方法,也可以称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
如下列代码,会先输出---1---,还是先输出--2--呢?
class Student():
def __init__(self):
print("---1---")
bruin = Student()
print("--2--")
答案是先输入---1---。因为在创建这个对象时,该方法九执行了。
上题的代码利用这个方法可以优化为这样:
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def info(self):
print(self.name,self.age)
ls = Student("李四",18)
ls.info()
10.__str__()方法
如果正在开发中,希望打印输入对象变量时,能够打印自定义的内容。就可以使用__str__()方法,将自定义的内容通过关键字 return 返回。(注意:返回值必须是字符串)
三.私有属性与私有方法
1.应用场景
在以下的学习中,我们了解到定义在__init__()构造方法初始化对象的属性是可以在类的外部访问到。那在实际开发中,对象的某些属性或方法,可能只希望在对象的内部使用,这时,我们就可以使用私有属性和私有方法。
2.私有属性
私有属性就是对象不希望公开的属性。
定义方式:在属性名前面增加两个下划线,例如:__name。
练习:
(1)定义类:“人”类
(2)创建对象:rose
(3)初始化对象属性:name和age
(4)要求:age不能在类的外部访问
class Person:
def __init__(self):
self.name = "肉丝"
self.__age = 20 # 私有属性定义
rose = Person()
print(rose.name) # "肉丝"
print(rose.__age) # AttributeError: 'Person' object has no attribute '__age'
那如果有需求一定要在类的外部访问到对象的私有属性时,我们可以使用如下步骤:
(1)可以通过类的内部的方法访问私有属性;
(2)通过类的内部方法将私有属性返回出去;
(3)在类的外部调用该方法并且接收就可以。
class Person:
def __init__(self):
self.name = "肉丝"
self.__age = 20 # 私有属性定义
def test(self):
# print(self.__age) # 在类的内部 可以访问 私有属性
return self.__age
rose = Person()
rose_age = rose.test()
print(rose_age,'test') # 20 test
3.私有方法
私有方法就是对象不希望公开的方法。
定义方式:在方法名前面加上两个下划线(例如:_test)
代码如下:
class Demo:
def test1(self):
print("--1--")
def __test2(self): # 私有方法
print("--2--")
def test3(self):
print("--3--")
d = Demo()
d.test1()
d.__test2() # AttributeError: 'Demo' object has no attribute '__test2'
d.test3()
注意:在python中,并没有真正意义上的私有,只有伪私有。当我们在访问时,以对象._类名__私有属性名称即可访问,私有方法也一样。
class Person:
def __init__(self):
self.name = "肉丝"
self.__age = 20 # 私有属性定义
rose = Person()
print(rose._Person__age) # 20
但是同样也需要注意的是,在实际开发中,不要通过该方式访问对象的私有属性或私有方法。
四.成员
1.成员介绍:类的成员可分为字段,方法,以及属性。
五.字段
1.字段介绍:我们学习到过的属性,更准确来说被称为字段。字段又主要分为普通(实例)字段和静态(实例)字段。
2.普通字段(实例属性)
普通字段属于对象,保存在对象中,只能通过对象访问。
(1)定义:self.字段名称
(2)访问:self.字段名称或对象.字段名称
练习:定义一个省份类,打印输出班级每位同学所在的国家与省份。代码如下:
class Province(object):
def __init__(self,country,name):
self.country = country
self.name = name
def print_info(self):
print(self.country,self.name)
hunan = Province("中国","湖南")
shandong = Province("中国","山东")
hunan.print_info()
shandong.print_info()
但是如果这样实现,会有相应的问题。比如说,普通字段保存在对象中,也就是说,当创建一个对象时,就会重新创建 self.country和self.name字段。当存对象的字段值相同时,仍然使用普通字段便会造成我们的内存浪费。所以这时候我们引用了静态字段。
3.静态字段(类属性)
静态字段属于类,保存在类中。在创建时,仅创建一份,并且所有对象都可以共享静态字段。执行时可以是类访问也可以是对象访问。
(1)定义:直接在类中定义
(2)访问:类名.字段名称 或 self.字段名称 或对象.字段名称
练习优化:
class Province(object):
country = "中国" # 静态字段 类属性
def __init__(self,name):
self.name = name # 普通字段
def print_info(self):
print(Province.country,self.name)# 静态字段 通过类名访问
hunan = Province("湖南")
shandong = Province("山东")
hunan.print_info()
shandong.print_info()
4.总结
在实际开发中,如果字段的值是固定的,不随对象的改变而改变。那就使用静态字段(类属性),这样有利于节省内存的消耗。而如果字段的值是随对象而改变,就使用普通字段(实例属性)。
六.方法
1.方法介绍:类的方法主要可分为普通方法(实例方法),类方法,静态方法。
2.普通方法(实例方法)
普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必须为系统内建参数,默认为self,代指对象本身。
注意:self仅仅是变量名。使用其它也可,但最好不要修改。
(1)定义即访问如下代码:
class Demo(object):
def __init__(self):
self.name = "肉丝"
# 普通方法(实例方法)
def test(self):
print(self.name)
rose = Demo()
rose.test() # 实例化后 通过 对象 调用
(2)应用场景:当方法中需要使用普通字段(实例对象)时,通常使用普通方法。
3.静态方法
静态方法通常在方法上面添加 @staticmethod 修饰器定义,保存在类中。静态方法不需要传入self参数也并不像普通对象那样代指对象本身,仅仅是一个普通的形参。
静态方法的调用虽然可以通过对象来调用,但是一般由类调用。
(1)定义访问如下:
class Demo:
def __init__(self):
self.name = "rose"
self.age = 18
def test(self):
print(self.name)
@staticmethod # 通过@staicmethod装饰器将方法转为静态方法
def stat_test(): # 静态方法 self不是必须加的,加了也不止对象
print("我是静态方法")
rose = Demo()
rose.test()
Demo.stat_test() # 一般通过类名调用
(2)应用场景
静态方法的参数并不代表对象之一特点,使静态方法在类中使用起来更像一个单纯的函数。完全可以将静态方法放在类的外部,当成函数使用。但是放在类中,便于使用与维护。
练习:在类中定义一个打印输出当前时间的方法。
import time
class TimeTest:
@staticmethod
def showtime():
print(time.strftime("%H:%M:%S", time.localtime()))
nowtime = TimeTest()
TimeTest.showtime() # 通过 类名 调用 静态方法
4.类方法
类方法通过方法上面添加 @classmethod 修饰器,保存在类中。类方法不需要传入self,但有一个系统自建参数为 cls,cls代指类本身。类方法一般通过类调用,也可通过对象调用。
(1)定义及访问如下:
class Demo:
@classmethod # 通过@classmethod装饰器将方法转为静态
def clasmd(cls): # 自建cls参数 代指 当前类
print(cls) #
d = Demo()
Demo.clasmd() # 通过 类名 调用
5.总结
一般而言,访问字段:对象.字段名称;访问方法:对象.方法名称([参数])。
七.属性
1.定义:在实际开发中,为了简便,当想访问方法以属性的访问形式时,就可以通过在方法上面添加 @property 装饰器,达到该效果。
2.定义及访问如下:
class Demo:
def __init__(self):
self.name = "rose"
@property
def test(self):
print(self.name)
d = Demo()
d.test # 通过@property 以 普通属性 形式 访问 普通方法
注意:以上 @property 只相当于一个只读属性,也就是说,仅能访问test函数内容。
练习:(1)通过传参改变 self.name的值;(2)删除掉self.name
class Demo:
def __init__(self):
self.name = "rose"
@property
def test(self): # 只可访问模式
print(self.name)
@test.setter # d.set_test = "jack"
def test(self,para):
self.name = para
print(self.name)
@test.deleter
def test(self): # del d.test
del self.name
print(self.name) # 报错 self.name已被删除
d = Demo()
d.test # 通过@property 只读模式
# 改变self.name的值
d.test = "jack" # 给方法传参 以 赋值方式 会触发@test.setter
# 删除self.name
del d.test # del 会 触发 @test.deleter
八.面对对象的三大特征
面对对象三大特征是指:封装,继承与多态。
九.封装
1.封装介绍
封装是面对对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或方法,隐藏功能的实现细节,也可以设置访问权限。
代码展示:
class Student:
def __init__(self,name,age):
self.name = name # 字段封装到了类的内部
self.age = age
def prin_info(self):
# print(ls.name,ls.age)
print(self.name,self.age)
ls = Student("李四",18)
# ls.name = "李四" # 字段(属性)定义在类的外部
# ls.age = 18
ls.prin_info()
十.继承
1.继承介绍:继承是一种创建新类的方式,如果子类需要复用父类的属性和方法时,就可以使用继承。当然,子类也可以提供自己的属性和方法。
比如:
(1)继承格式
class Father: # 父类 超类
pass
class Son(Father): # 子类 派生类
pass
注意:在python中,新建的类可以继承一个或多个类。
(2)作用:避免重复造轮子,减少代码冗余。
2.新式类与经典类
在python2当中类分为新式类和经典类,如果有继承父类object则是新式类,否则为经典类。
但是在python3中,全部都是新式类,默认继承object。
练习:验证python3中,全部都是新式类。
(1)实现思路:比较继承与无继承两个空类的成员是否一致。
(2)扩展方法:对象.__dir__()查看对象的属性与方法。
class Father(object):
pass
class Son:
pass
f = Father()
s = Son()
print(len(f.__dir__())) # 26
print(len(s.__dir__())) # 26
3.单继承
子类继承父类,则可以直接享受父类中已经封装好的方法。
练习:用代码实现下图逻辑(自行练习)
class GrandFather(object):
def sleep(self):
print("GrandFather sleep")
class Father(GrandFather):
def eat(self):
print("Father eat")
def drink(self):
print("Father drink")
class Son(Father):
def study_py(self):
print("Son stupy python")
s = Son()
s.study_py()
s.eat()
s.sleep()
总结:当对象调用方法时,查找顺序先从自身类找,如果没找到,则从父类中找,父类中没有,再到父类的父类找,直到找到object类,若还是没有,则报错。这也被称为深度优先机制。
但是需要注意的是,当子类与父类拥有同名称的方法时,子类对象调用该方法优先执行自身的方法。那么实际上就是子类的方法覆盖父类的方法,也被称为重写。
代码如下:
class GrandFather(object):
def sleep(self):
print("GrandFather sleep")
class Father(GrandFather):
def eat(self):
print("Father eat")
def drink(self):
print("Father drink")
class Son(Father):
def study_py(self):
print("Son stupy python")
def sleep(self):
print("Son sleep")
s = Son()
s.study_py()
s.eat()
s.sleep() # Son sleep
但是实际开发中,遵循开放封闭的原则,我们并不会完全的重写父类的方法,而是希望同时实现父类的功能。这时,我们就希望调用父类的方法了,可以通过super()函数实现。
4.super()
super()函数是用于调用父类(超类)的一个方法。
(1)语法:super(type[, object-or-type])
- type-->类
- object-or-type -- 类,一般是 self
练习:继以上的练习实现,在Son的sleep方法当中,调用父类的sleep方法。
class Son(Father):
def study_py(self):
print("Son stupy python")
def sleep(self):
print("Son sleep")
# super(Son,self).sleep() # Son的对象去调用父类的sleep方法
super().sleep()
总结:
(1)继承 深度优先 先从自己找 找不到则从父类找
(2)重写(防止执行父类中的方法)
(3)self永远是执行该方法的调用者
(4)super(子类,self).父类中的方法(arg)或者父类名.父类中的方法(self,arg)
注意:__init__方法也会继承2=,同实例方法一致;私有属性以及私有方法不会被继承。
5.多继承
所谓多继承,即子类有多个父类,并且具有它们的特征。
(1)情景1
(2)情景2
(3)情景3
十一.多态
多态的概念是应用于Java和C#这一类强类型语言中,而python崇尚“鸭子类型“
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的”鸭子类型“,它并不要求严格的继承体系,一个对象只要”看起来像鸭子,走起路来像鸭子“,那它就是一个鸭子。
所谓多态:定义时的类型和运行时的类型不一样,此时就称为多态。
十二.魔法方法
1.魔法方法介绍
在python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动调用,称为魔法方法。魔法方法的命名总是被双下划线包围,比如__名称__。
2.常见魔法方法
(1)__doc__:用来查看类的说明文档。
练习:查看列表类的说明文档。
print(list().__doc__)
查看自定义类的说明文档
class Demo(object):
"""
我是注释
"""
pass
d = Demo()
print(d.__doc__)
(2)__module__:用来查看当前操作的类所属于的模块。
总结:当执行模块是类所在模块时,执行结果为__main__。否则,执行结果是类所在模块的名称。
(3)__class__:用来查看当前对象的类。
总结:类也是对象,Demo是type类的对象,d是Demo类的对象。
(4)__dict__:用于获取类或者实例的属性字典
注意:普通字段存储在对象中,所以通过对象.__dict__获取的是普通字段。除普通字段以外的成员都存储在类中,所以通过类.__dict__来获取。
(5)__del__():__del__()方法也叫做析构方法。当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。
总结:当代码全部执行完毕才自动触发__del__();如果需要提前触发,则需要通过del关键字,删除所有对象后触发__del__()。
注意:此方法一般不需要定义,因为python是一门高级的的语言,程序员在使用时无需担心内存的分配与释放,一般都是交给python解释器来执行。所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行。
(6)__call__()方法:__call__()方法用于将对象变成一个可调用的对象。也就是说,当一个类中有__call__()方法时,其实例化得到的对象便是可调用的(callable)。
class Demo(object):
pass
d = Demo()
d() # TypeError: 'Demo' object is not callable
(7)__new__()方法:__new__()方法用于创建与返回一个对象。再类的准备将自身实例化时调用。
练习:
以下代码打印输出的顺序? C
A.__init__,__new__
B.__init__
C.__new__
D.__new,__init__
class Demo(object):
def __init__(self):
print("__init__")
def __new__(cls, *args, **kwargs):
print("__new__")
d = Demo()
注意:__new__()方法用于创建对象;__init__()方法在对象创建的时候,自动调用;但是这里重写了父类的__new__()方法,覆盖了父类__new__()创建对象的功能,所以对象并没有创建成功。所以仅执行__new__()方法内部代码。
对象创建执行顺序:1.通过__new__()方法创建对象;2.并将对象返回,传给__init__()。
练习:在自定义类中实现创建对象。
思路:(1)重写父类__new__()方法;(2)并且在该方法内部,调用父类__new__()方法。
class Demo(object):
def __init__(self):
print("__init__")
def __new__(cls, *args, **kwargs):
print("__new__")
return super().__new__(cls)
d = Demo()
注意:在创建对象时,一定要将对象返回,在会自动触发__init__()方法;__init__()方法当中的self,实际上就是__new__返回的实例,也就是该对象。
(8)__init__()与__new__()的区别:
- __init__实例方法,__new__静态方法
- __init__在对象创建后自动调用,__new__创建对象的方法
十三.单例模式
1.单例模式介绍
单例模式是一种常用的软件模式。也就是说该类只包含一个实例。
通过单例模式可以保证系统中的一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
通常应用在一些资源管理器中,比如日志记录等。
2.单例模式实现
思路:
(1)当对象不存在时,创建对象;
(2)当对象存在时,永远返回当前已经创建对象。
class single(object):
__isinstance = None
def __new__(cls, *args, **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))
十四.反射
在实际开发中,当开发者想要执行对象里的方法或者属性时,但是无法确定该方法及属性一定存在。这时就需要使用一些特殊的方法来访问与操作这个未知的方法或属性,那么此时程序员机会使用反射处理。
1.反射介绍
反射实际上就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,是一种基于字符串的事件驱动。



