面向对面
一切皆为对象
面向过程 面向对象的个人理解
以搬家具到屋子为例
面向过程分析这个事 把事情分成几步 从上到下按照流程走下去自然就完成了 这个也是我们常见的逻辑思维思路
1。人去搬动家具 2 移动到屋子 3 重复 1 2步若干次 4 所有家具都搬进去了或者屋子满了为止 5流程结束
面向对象分析这事 首先分析 办这个事的主体都有谁 然后这个事 需要这几个对象都有什么特点 然后就是这些对象的特点怎么样交互才能完成这事
对象:
1 人对象 2 家具对象 3 屋子对象
要办这事这些对象之间需要什么特点:
1 人对象需要有搬动这个功能 2 家居对象 需要有个占地面积的功能还有家具编号 3 屋子对面需要有总面积 剩余面积的特点
交互来完成目标(利用这几个对象和对象自身的特点来进行交互以达到目标):
人这个对象的搬动操作 搬动目标是家具对象n 搬动到的地方是屋子对象 判断完成条件是 所有家具都搬动过了 或者 屋子对面的剩余面具属性小于等于0 其实交互也可以部分看作是按照流程 面向过程
类与对象
类就是汽车图纸 这个类的对象 就是按照这图纸制造的一辆一辆的汽车
定义类
classTestClassObject(object):"""展示下新类的常见结构"""name= "TestClassObject.name" #类属性 name 可以在该类的所有实例下调用 但是只能通过类名.类属性进行修改 不能类实例.类属性进行修改
fun = "测试用类" #类属性 fun
def __init__(self, a, b, c): #固定的类初始化函数 在类实例化的时候自动隐式调用 该类方法内也是统一声明类实例属性的地方
self.a = a #类实例属性 a
self.b = b #类实例属性 b
self.c = c #类实例属性 c
def test1(self): #类实例方法 第一个参数是self 但是调用类实例方法第一个参数不用写
print(self.a +self.b)
@classmethoddef test2(cls): #这是类方法 第一个参数代指类本身 可以在这个类方法里面调用类属性 其他类方法来运算
print(cls.name)
@staticmethoddef test4(): #这是静态方法 注意这个静态方法不能有 类的属性方法 或者 类实例的属性和方法在 看做是一个普通的函数即可
print("这里是静态方法")
Python __init__()类构造方法
1.每次创建对应实例就会自动隐式调用__init__()
2 第一个参数必然是self 类方法的参数self代表的是类实例本身 注意不是类本身 类本身用cls来代表
3 普通的类实例属性一般都在init里面声明 也是规范的类实例化赋初值的地方
4 类属性和类实例属性
(1)类属性和类实例属性的区分: 定义在类方法外 但是依然在类内声明的就是类属性 类实例属性是定义在类方法里面并且以self.变量名声明和使用的的都是类实例属性 还有一种是在类方法中声明 但是只是以 变量名 = 值 的形式的只是一个单纯的局部变量 一般而言统一规范 类实例属性都在init方法里面声明初值
(2)修改类属性:类属性是在该类下的所有实例都共享该类属性 都可以通过类实例.类属性名 获取到类属性的内容 但是 无法通过类实例.类属性名 = 新值 来修改类属性的内容 修改类属性 还是要以 类名.类属性 = 新值 来修改
(3)修改类实例属性: 自然是 类实例.类实例属性 = 新值
类实例 或者类对象的创建
类的实例化
实例名1 = 类名(初始化的参数1,初始化的参数2,初始化的参数3,…)
类实例的使用
实例名1.类实例属性
实例名1.类实例方法(参数)
类实例属性的添加和删除
添加:直接在代码中写 实例名1.新属性名 = 值 就好 不过这种写法太随意了 不太规范 还是在类内提前声明好就是 也方便管理代码
删除:del(实例名1.新属性名)
类方法 类实例方法 静态方法
类方法:
1.第一个参数必须是cls 类方法可以使用类属性和其他类方法 但是不能使用 类实例属性和方法 因为cls代指类本身 而不是类实例
2. 调用 类名.类方法()
3 类方法定义的时候开头加上@classmethod
类实例方法:
1.第一个参数必须是self 类实例方法可以使用 类实例属性和类实例方法 但是切不可以使用 类属性类方法
2 调用 类实例名.类实例方法() 还有一种未绑定方法 类名.类实例方法(实例名,参数1,参数2….)
静态方法:
1.参数不能包含cls self 方法内不能调用 类属性类方法 类实例属性 类实例方法 可以理解为一个在类内的普通函数
2 调用 类名.静态方法() 类实例名.静态方法()
3 类方法定义的时候开头加上@staticmethod
classTestClassObject(object):"""展示下新类的常见结构"""name= "TestClassObject.name" #类属性 name 可以在该类的所有实例下调用 但是只能通过类名.类属性进行修改 不能类实例.类属性进行修改
fun = "测试用类" #类属性 fun
def __init__(self, a, b, c): #固定的类初始化函数 在类实例化的时候自动隐式调用 该类方法内也是统一声明类实例属性的地方
self.a = a #类实例属性 a
self.b = b #类实例属性 b
self.c = c #类实例属性 c
def test1(self): #类实例方法 第一个参数是self 但是调用类实例方法第一个参数不用写
print(self.a +self.b)
@classmethoddef test2(cls): #这是类方法 第一个参数代指类本身 可以在这个类方法里面调用类属性 其他类方法来运算
print(cls.name)
@staticmethoddef test3(): #这是静态方法 注意这个静态方法不能有 类的属性方法 或者 类实例的属性和方法在 看做是一个普通的函数即可
print("这里是静态方法")#调用类属性 类方法: 类名.类属性 类名.类方法() 不推荐 类实例名.类属性 类实例名.类方法()
print("类属性为:{}".format(TestClassObject.name))#类方法
TestClassObject.test2()#实例属性
print("类实例属性为:{}".format(test_class_object.a))#类实例方法
test_class_object.test1()#类静态方法 类名.静态方法() 类实例名.静态方法() 都可以
TestClassObject.test3()
test_class_object.test3()
封装
Python下 没有public 和 protected private
1.public :默认情况下 类属性 类方法 类实例属性 类实例方法 静态方法 都是public public下 类内可用 类外 和子类中都可以直接使用
2 protected:属性方法前加 _ 就是protected 能在本类和子类中使用 但是类外直接访问不行
3.private :属性方法 名字前加上 __则表示为private 只能在本类中使用 在类外或者子类中都无法使用4.__doc__ __init__ __new__之类的固定的类方法 不是私有 只是固定的写法
5.封装函数property() 用于给私有属性提供进一步的封装控制
私有属性=property(读取该私有属性触发的类方法,[修改该私有属性触发的类方法],[del该私有属性触发的类方法],【读取该私有属性__doc__触发的类方法],)
这4个参数不是都必须写的 可以写一个 可以写2个 3个 4个 分别用来控制 外界或者子类访问私有属性触发的函数 比如我们只要让他有只读权限 就只设置第一个参数 其他不管即可
6.如果知道了 类名 和私有属性或者私有方法真正的名 我们可以用 实例名._类名私有属性名 的方式直接访问修改 私有属性 比如下面的testaa._TestProperty__aa的用法
classTestProperty(object):"""测试私有属性的Property的用法"""
def __init__(self, aa):
self.__aa = aa #给私有属性赋初值
#外界想要用 类实例名.变量名的方式来获取这个私有变量的时候触发 注意 类实例名.变量名的变量名 以下面的 变量名=property 为准
defgetaa(self):print("=[获取]私有属性aa的值=")return self.__aa
#外界想要用 类实例名.变量名的方式来修改这个私有变量的时候触发
defsetaa(self, v):print("=[修改]私有属性aa的值=")
self.__aa =v#外界想要用 del(类实例名.变量名)的方式来修改这个私有变量的时候触发 注意的是del函数无法真正的销毁这个私有属性 要想彻底销毁这个私有属性 需要在类内执行del才可以
defdelaa(self):print("=[删除]私有属性aa的值=")#del(self.__aa) # 要想彻底销毁这个私有属性 需要在类内执行del才可以
self.__aa = 0 #假装销毁一下归零吧
#这个变量才是外面要直接调用的变量名 而不是私有属性名 这个变量可以随意改
aaa = property(getaa, setaa, delaa, "doc1111")#aaa = property(getaa) #设置为外界只读
testaa= TestProperty(14)print("私有类属性aa为:{}".format(testaa.aaa))
testaa.aaa= 24
print("私有类属性aa为:{}".format(testaa.aaa))del(testaa.aaa)print("私有类属性aa为:{}".format(testaa.aaa))print(testaa._TestProperty__aa)
testaa._TestProperty__aa= 34 #知道了类名和私有属性名 用这种方式可以直接修改访问私有属性
print("私有类属性aa为:{}".format(testaa.aaa))
public protected private三种封装类型 在 当前类 当前类的子类 和外部访问的权限情况
范围\封装类型
public
protected
private
类内
okok
ok
外部访问
ok
无法直接访问修改
无法直接访问修改
子类是否继承到这些封装属性方法
ok
ok
虽然dir(子类)显示有该私有属性方法但是无法在子类中调用该继承属性方法
子类中能否直接调用父类.封装属性方法
ok
ok
调用失败 没找到对应的属性方法
结论:public是哪个地方都能用 protected是类内 子类能继承 子类也能直接调用 但是就是外部无法直接访问 private是只能类内调用 子类继承这种私有属性方法但无法使用 子类更无法直接使用这种父类的私有属性方法
继承
单继承
class TestObject(object): # 括号内是父类名字
pass
多继承
class TestObject(TestA, TestB): # 括号内是父类们的名字
pass
多继承的问题
一个子类继承多个父类,如果这些父类的方法属性重名相互冲突 一般都是前面的父类的同名方法属性覆盖后面的 并且并不推荐使用多继承
子类的属性方法的来源
由于存在继承 类实例使用的属性方法 可能是源于类本身 也可能是源于类的父类中 或者源于 父类的父类 祖父类 等等一级一级的向上找 都是有可能的 至于先找谁后找谁就涉及到了 mro问题
父类的方法的重写
没什么说的
class TestFather(object):
def hobby(self): # 父类的爱好方法是喜欢喝酒
print(“爱好是喝酒”)
class Tom(TestFather): # 但是tom这个不喜欢喝酒 但是会自动继承父类 TestFather的爱好方法 所以这里重写下 这个爱好方法改变他的内容
def hobby(self):
print(“爱好是睡觉”)
非绑定方法
解决通过类名调用该类对应的实例方法 通过类名调用实例方法 叫非绑定方法 具体形式 类名.实例方法名(实例名,参数1,参数2….)这样可以使用类名来调用实例方法注意严格按照实例方法的参数来写入参数 一个都不能少 不会自动省略self了 如果该实例方法内部还掺杂 其他实例属性或者其他实例方法无法在参数里面完全给予 则只能另想其他思路
super()函数
解决 子类调用父类中的方法的问题 一般是用来载入父类的__init__方法 而且注意父类的__init__()的参数个数 也要原样的写上 如果是多继承,那么除了第一个父类可以用super().父类方法(参数1.参数2,…) 其他的依然要使用 父类名.父类方法(参数1.参数2,…) 来调用
注意 super在多继承情况下 super是代表当前类第一个父类 根据该父类的方法 super自动加载self 这个不用写 其他的参数都要写
个人推荐 子类调用父类中的方法上 如果单继承 推荐super 如果多继承 第一个父类的方法都用super执行 其他的父类则推荐 父类名.父类方法名(参数1,…) 注意这种写法严格按照实例方法的参数来写入参数 一个都不能少 不会自动省略self了
classPeople:def __init__(self,name):
self.name=namedefsay(self):print("我是人,名字为:",self.name)classAnimal:def __init__(self,food):
self.food=fooddefdisplay(self):print("我是动物,我吃",self.food)classPerson(People, Animal):#自定义构造方法
def __init__(self,name,food):#调用 People 类的构造方法
super().__init__(name)#super(Person,self).__init__(name) #执行效果和上一行相同
#People.__init__(self,name)#使用未绑定方法调用 People 类构造方法
#调用其它父类的构造方法,需手动给 self 传值
Animal.__init__(self,food)
per= Person("zhangsan","熟食")
per.say()
per.display()结果我是人,名字为: zhangsan
我是动物,我吃 熟食
了解:动态的添加或者修改类 或者 类实例的属性和方法
类名.新方法名 = 函数名
类实例名.新方法名 = 函数名
但是这种动态的添加修改 类属性 类方法 实例属性 实例方法的有点危险 可以在类内规定 运行动态添加修改的属性名或者方法名 但是类的动态添加修改不在限制范围内class CLanguage:
__slots__ = ('name','add','info') # 值允许添加修改这3个命名的属性或者方法 而且只能限制类实例 类本身的动态添加属性方法不受限制 并且对于本类的子类 也没有继承到这个限制
了解:type ()函数的进一步的用法 创建自定义类(创建一个新的类型) type(name, bases, dict)
#定义一个实例方法
defsay(self):print("我要学 Python!")#使用 type() 函数创建类
CLanguage = type("CLanguage",(object,),dict(say = say, name = "C语言中文网"))#创建一个 CLanguage 实例对象
clangs =CLanguage()#调用 say() 方法和 name 属性
clangs.say()print(clangs.name)
从底层原理看 我们最常见的用class定义类 到了底层本质上就是用type来声明一个类的
多态
1.多态发生在有继承关系的子类父类之间
2.子类重写了父类的某方法 使该方法的结果和父类不同呈现多种形态classCLanguage:defsay(self):print("调用的是 Clanguage 类的say方法")classCPython(CLanguage):defsay(self):print("调用的是 CPython 类的say方法")classCLinux(CLanguage):defsay(self):print("调用的是 CLinux 类的say方法")
a=CLanguage()
a.say()
a=CPython()
a.say()
a=CLinux()
a.say()调用的是 Clanguage 类的say方法
调用的是 CPython 类的say方法
调用的是 CLinux 类的say方法
类常见的几个固定函数
object类的理解
Python3下所有没指定父类的类默认都继承object 就是所说的新类 Python3之前的都是经典类
没继承和继承了object的区别如下Person ['__doc__', '__module__', 'name'] #没有继承object的普通景点类 就2个类方法 name是自己定义的类实例属性
Animal ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name'] #这个就是继承了object
object类的类属性方法的简单介绍
classobject:"""The most base type"""
#del obj.xxx或delattr(obj,'xxx')时被调用,删除对象中的一个属性
def __delattr__(self, *args, **kwargs): #real signature unknown
"""Implement delattr(self, name)."""
pass
#对应dir(obj),返回一个列表,其中包含所有属性和方法名(包含特殊方法)
def __dir__(self, *args, **kwargs): #real signature unknown
"""Default dir() implementation."""
pass
#判断是否相等 equal ,在obj==other时调用。如果重写了__eq__方法,则会将__hash__方法置为None
def __eq__(self, *args, **kwargs): #real signature unknown
"""Return self==value."""
pass
#format(obj)是调用,实现如何格式化obj对象为字符串
def __format__(self, *args, **kwargs): #real signature unknown
"""Default object formatter."""
pass
#getattr(obj,'xxx')、obj.xxx时都会被调用,当属性存在时,返回值,不存在时报错(除非重写__getattr__方法来处理)。
#另外,hasattr(obj,'xxx')时也会被调用(估计内部执行了getattr方法)
def __getattribute__(self, *args, **kwargs): #real signature unknown
"""Return getattr(self, name)."""
pass
#判断是否大于等于 greater than or equal,在obj>=other时调用
def __ge__(self, *args, **kwargs): #real signature unknown
"""Return self>=value."""
pass
#判断是否大于 greater than,在obj>other时调用
def __gt__(self, *args, **kwargs): #real signature unknown
"""Return self>value."""
pass
#调用hash(obj)获取对象的hash值时调用
def __hash__(self, *args, **kwargs): #real signature unknown
"""Return hash(self)."""
pass
def __init_subclass__(self, *args, **kwargs): #real signature unknown
"""This method is called when a class is subclassed.
The default implementation does nothing. It may be
overridden to extend subclasses."""
pass
#object构造函数,当子类没有构造函数时,会调用object的__init__构造函数
def __init__(self): #known special case of object.__init__
"""Initialize self. See help(type(self)) for accurate signature."""
pass
#判断是否小于等于 less than or equal,在obj<=other时调用
def __le__(self, *args, **kwargs): #real signature unknown
"""Return self<=value."""
pass
#判断是否小于 less than,在obj
def __lt__(self, *args, **kwargs): #real signature unknown
"""Return self
pass
#创建一个cls类的对象,并返回
@staticmethod #known case of __new__
def __new__(cls, *more): #known special case of object.__new__
"""Create and return a new object. See help(type) for accurate signature."""
pass
#判断是否不等于 not equal,在obj!=other时调用
def __ne__(self, *args, **kwargs): #real signature unknown
"""Return self!=value."""
pass
def __reduce_ex__(self, *args, **kwargs): #real signature unknown
"""Helper for pickle."""
pass
def __reduce__(self, *args, **kwargs): #real signature unknown
"""Helper for pickle."""
pass
#如果不重写__str__,则__repr__负责print(obj)和交互式命令行中输出obj的信息
#如果重写了__str__,则__repr__只负责交互式命令行中输出obj的信息
def __repr__(self, *args, **kwargs): #real signature unknown
"""Return repr(self)."""
pass
#使用setattr(obj,'xxx',value)、obj.xxx=value是被调用(注意,构造函数初始化属性也要调用)
def __setattr__(self, *args, **kwargs): #real signature unknown
"""Implement setattr(self, name, value)."""
pass
#获取对象内存大小
def __sizeof__(self, *args, **kwargs): #real signature unknown
"""Size of object in memory, in bytes."""
pass
#设置print(obj)打印的信息,默认是对象的内存地址等信息
def __str__(self, *args, **kwargs): #real signature unknown
"""Return str(self)."""
pass@classmethod#known case
def __subclasshook__(cls, subclass): #known special case of object.__subclasshook__
"""Abstract classes can override this to customize issubclass().
This is invoked early on by abc.ABCMeta.__subclasscheck__().
It should return True, False or NotImplemented. If it returns
NotImplemented, the normal algorithm is used. Otherwise, it
overrides the normal algorithm (and the outcome is cached)."""
pass
#某个对象是由什么类创建的,如果是object,则是type类
__class__ =None#将对象中所有的属性放入一个字典,例如{'name':'Leo','age':32}
__dict__ ={}#类的doc信息
__doc__ = ''
#类属于的模块,如果是在当前运行模块,则是__main__,如果是被导入,则是模块名(即py文件名去掉.py)
__module__ = ''
__new__ __init__ __str__ __del__的介绍
类初始化的机制的粗浅理解
首先开始初始化->__new__被触发->类本身作为参数被传入到__new__里面->使用继承自object类或者父类的__new__方法 在内存中开辟了一个空间来准备存储这个类的实例对象,并且把开辟的空间地址(也有一说是返回的一个实例对象) 自动传给__init__作为它的第一个参数并自动调用了__init__->__init__方法在__new__开辟的空间进行了进一步的初始化工作 完成整个初始化 整个初始化是__new__和__init__一起配合才完成的
__new__
初始化对象的时候触发 __new__是个类方法 参数是类本身(参数cls) 作用是创建一个类实例的空间 返回一个类实例对象自动给__init__作为第一个参数 并触发__init__继续完成初始化
__init__
承接__new__继续完成初始化工作 没有返回值 是个类实例方法
一般情况下都是在__init__里面显式对类实例属性统一的声明赋初值和进行前期处理和验证
__str__
print函数的时候触发 简单说 print(类实例名) 其实就是自动调用了 类实例.__str__ 默认情况下会输出 实例名和对应的内存地址 如果我们改写__str__那么print(类实例名) 显示的内容自然是按照我们的定义来显示的
classStudent(object):def __init__(self,id,name,age):
self.id=id
self.name=name
self.age=agedef __str__(self):return "学号:{}--姓名:{}--年龄{}".format(self.id,self.name,self.age)
s=Student(111,"Bob",18)print(s)学号:111–姓名:Bob–年龄18 # 而不是
__del__
对象从内存删除的时候触发 比如该实例被del()了 自然会自动触发类实例.__del__() 没什么可说的
项目的简易分析设计
总结需求 –> 分析 -> 获取其中的各个对象 和对象具备的属性方法 –> 研究对象之间的交互 构建整个流程
项目简易分析 很简单 把需求写出来 然后提取出名词 动词 形容词 数据等等 一般情况下 名词就是该项目的一个对象 形容词很可能是某个对象的属性 动词很有可能是某个对象的方法 然后建立对象和对应的属性方法 然后尝试用对象和具备的属性方法来交互完成整个项目要求 如果存在问题再修正 不断完善
实例1:屋子放家具
1.2个名词 屋子 家具 不过按理说还有一个移动家具的 人存在 暂时是3个对象 屋子 家具 人
2 屋子没法自己把家具拉进去 家具自己也没张腿 所以 人 必须具备方法 搬动 这个搬动方法应该至少2个参数 一个是搬动什么 第二个参数是搬动到哪里去 家具存在多个 自然要增加一个 家具对象的属性 家具id和家具名字 屋子应该只有一个 不需要屋子序号 只要一个屋子名字属性即可
屋子 属性:屋子id 屋子名
人 方法:搬动
家具 属性:家具id 家具名字
3 要完成屋子放家具的动作 对象人.搬动(被搬动的家具id,搬动到哪的屋子id)
实例2:小明体重75公斤 每次跑步减少0.5 每次吃饭增加1
1。名词就一个小明 对象就小明一个对象 体重是附属于小明的自然是小明这个对象的属性 动词 跑步 吃饭 都是属于小明对象的方法
2 对象小明
属性:体重
方法: 跑步 吃饭
3 简单的概念结构
class xiaoming(object):
def __init__(self,weight):
self.weight = weight
def showweight():
print(self.weight)
def run():
self.weight –= 0.5
def eat():
self.weight += 1
设计模式 单例模式
设计模式很多 先来说下单例模式
简单说 无论创建多少类的实例 单例模式会让从始至终只有一开始创建的那个类实例存在
单例模式的实现主要是对 类的__new__的重写来实现的 每次要创建新的类实例 触发__new__的时候 在__new__里面都会检查下 之前记录的类实例的是否还存在 存在则返回这个已存在的实例对象的内存地址给__init__就是 如果不存在则创建下新的类实例 然后记录下这个类实例到类属性中 然后把新的类属性返回给__init__继续完成初始化的工作
一个简易的单例模式:没考虑多线程的问题 后面再说
classTestOnlyMode(object):"""简单的一个单例模式结构"""
#类属性 用来记录唯一的类实例对象和是否要初始化实例的各个参数
_only_record =None
_is_init=Falsedef __new__(cls, *args, **kwargs):ifcls._only_record:#_only_record里面已经存储了一个类实例了 就不用创建了
print("_only_record 为真 不用创建实例了[{}]".format(cls._only_record))pass
else:#还没创建实例呢 就现在创建1个
print("_only_record 为假 创建实例中")
cls._only_record= object.__new__(cls)print("_only_record 为真 创建实例完毕[{}]".format(cls._only_record))return cls._only_record #返回个实例对象给init处理
def __init__(self, a, b, c):print(self._is_init)ifself._is_init:#为真表示已经实例化完毕了 不用再次连续设置初值
print("无需初始化参数")pass
else:#没实例化自然要设置初值
print("初始化参数了")
self.a=a
self.b=b
self.c=c
self._is_init=Truedefshowit(self):print("a:{} ,b:{} ,c:{}".format(self.a, self.b, self.c))
aa= TestOnlyMode(1, 2, 3)print(aa.showit())
bb= TestOnlyMode(1, 2, 4)print(bb.showit())
cc= TestOnlyMode(2, 2, 3)print(cc.showit())print(id(aa))print(id(bb))print(id(cc))