Python面向对象OOP、类与对象、self、面向对象三大特性(封装、继承、多态)、Mixin设计模式、魔法函数、抽象类、自定义类

Python面向对象

oop思想

  • 接触到任意一个任务时,首先想到的是这个世界的构成,是由模型构成的
  • 几个名词
    • OO:面向对象
    • OOA:面向对象的分析
    • OOD:面向对象的设计
    • OOI:xxx的实现
    • OOP:xxx的编程
    • OOA->OOD->OOI:面向对象的实现过程

类和对象的概念

  • 类:抽象名词,代表一个集合,共性的事物
  • 对象:具象的事物,单个个体
  • 类跟对象的关系
    • 一个具象,代表某一类事物的某一个个体
    • 一个抽象,代表是一大类事物
  • 类中的内容,应该具有两个内容
    • 表明事物的特征,叫做属性
    • 表明事物功能或动作,成为成员方法

类的基本实现

  • 类的命名
    • 遵守变量命名的规范
    • 大驼峰(由一个或者多个单词构成,每个单词首字母大写,单词之间直接相连)
    • 尽量避开跟系统命名相似的
  • 如何声明一个类
    • 必须是有class关键字
    • 类有属性和成员方法构成,其他不允许出现
    • 成员属性定义可以直接使用变量赋值,如果没有值,需使用None
# 定义一个学生类,用来形容学生
class Student():
    pass # 只是占位,没有任何意义,且必须写
mingyue = Student()

class PythonStudent():
    name = None
    age = 18
    course = "Python"
    def doHomeWork(self):
        print("在做作业!")
        return None
        
yueyue = PythonStudent() # 实例化一个学生
print(yueyue.name)
print(yueyue.age)
yueyue.doHomeWork()
  • 实例化类

      变量 = 类名() # 实例化了一个类
    
  • 访问成员对象

    • 使用点操作符

        obj.成员属性名称
        obj.成员方法
      
  • 可以通过默认内置变量检查类和对象的所有成员

    • 对象所有成员检查

        obj.__dict__
        # dict前后各两个下划线
      
    • 类所有成员

        class_name.__dict__
      
class A():
    name = "jiujiu"
    age = 18
    
A.__dict__

>>>
mappingproxy({'__module__': '__main__',
              'name': 'jiujiu',
              'age': 18,
              '__dict__': <attribute '__dict__' of 'A' objects>,
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              '__doc__': None})

类和对象的成员分析

  • 类和对象都可以存储成员,成员可以归类所有,也可以归对象所有
  • 类存储成员时,使用的是与类关联的一个对象
  • 独享成员对象是存储在当前对象中
  • 对象访问一个成员时,如果该对象中没有该成员,尝试访问类中同名成员
  • 如果对象中有这个成员,一定使用对象中的成员
class A():
    name = "jiujiu"
    age = 18
    
    def say(self):
        self.name = "baba"
        self.age = 19
        
print(A.name) # A称为类实例 
print(A.age)
print(id(A.name))
print(id(A.age))
a = A()
print(a.name)
print(a.age)
print(id(a.name))
print(id(a.age))
## 说明:类实例的属性和其对象的实例的属性在不对对象的实例属性赋值的前提下,指向同一个变量

>>>
jiujiu
18
2412651057928
140719419683104
jiujiu
18
2412651057928
140719419683104
  • 创建对象的时候,类中的成员不会放入对象中,而是得到一个空对象,没有成员
  • 通过对象对类中成员重新复制或者通过对象添加成员时,对应成员会保存在对象中,而不会修改类成员

关于self

  • self在对象的方法中表示当前对象本身,如果通过对象嗲用一个方法,那么该对象会自动传入到当前方法的第一一个参数中
  • self并不是关键字,只是一个用于接收对象的普通参数,理论上可以用任何一个普通变量名代替
  • 方法中有self形参的方法成为非绑定类的方法,可以通过对象访问,没有self的是绑定类的方法
  • 只能通过类访问
class Student():
    name = "dana"
    age = 19
    
    def say(self):
        self.name = "yaona"
        self.age = 17
        print("My name is {0}".format(self.name))
        print("My age is {0}".format(self.age))
    def sayAgain():	# 没有self
        print("nice to see you again")
        
s = Student()
s.say()
Student.sayAgain()	# 只能通过类这样访问,对象访问报错如下
s.sayAgain()

>>>
My name is yaona
My age is 17
nice to see you again
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-e8f7fdbe1bcb> in <module>()
     14 s.say()
     15 Student.sayAgain()
---> 16 s.sayAgain()

TypeError: sayAgain() takes 0 positional arguments but 1 was given
  • 使用类访问绑定类的方法时,如果类方法中需要访问当前类的成员,可以通过__class__成员名来访问
class Student():
    name = "dana"
    age = 19
    
    def say(self):
        self.name = "yaona"
        self.age = 17
        print("My name is {0}".format(self.name))
        print("My age is {0}".format(self.age))
    def sayAgain():
        print(__class__.name)	#
        print(__class__.age)	#访问类成员
        print("nice to see you again")
        
s = Student()
s.say()
Student.sayAgain()

>>>
My name is yaona
My age is 17
dana
19
nice to see you again
class A():
    name = "dana"
    age = 18
    
    def __init__(self):# 这是构造函数
        self.name = "aaaaa"
        self.age = 200
        
    def say(self):
        print(self.name)
        print(self.age)
        
class B():
    name = "bbbb"
    age = 89
    
a = A()
a.say()  # 此时,系统会默认把a作为第一个参数传入函数
# A.say() 这个会报错,因为say函数要默认传入一个对象,但是A是一个类,因此函数就缺少参数而报错
A.say(a) # 此时手动传入一个参数a
A.say(A) # 此时A会被手动传入
A.say(B) # 此时B被手动传入
# 上面这个叫做鸭子模型:不管是不是鸭子,只要具有鸭子的一些特征,就认为是鸭子

>>>
aaaaa
200
aaaaa
200
dana
18
bbbb
89

面向对象三大特性

  • 封装
  • 继承
  • 多态

封装

  • 就是对对象成员进行访问限制
  • 封装的三个级别
    • 公开 public
    • 受保护的 protected
    • 私有的 private
    • public,protected,private不是关键字
  • 判断对象的位置
    • 对象内部
    • 对象外部
    • 子类中
  • 私有
    • 私有成员是最高级别的封装,只能在当前类和对象中使用
    • 在成员前面添加两个下划线即可
class Person():
    name = "dana" # 这是共有的
    __age = 18    # 这是私有的
    
p = Person()
print(p.name)
print(p.__age) # 报错

>>>
dana
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-34-052c55c92458> in <module>()
      5 p = Person()
      6 print(p.name)
----> 7 print(p.__age) # 报错

AttributeError: 'Person' object has no attribute '__age'
  • python的私有不是真私有,是一种name mangling改名政策
  • 可以使用对象._classname__attributename访问
# name mangling技术
print(Person.__dict__)
# 还是可以访问到的,如下
p._Person__age = 90
print(p._Person__age)

>>>
{'__module__': '__main__', 'name': 'dana', '_Person__age': 18, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
90
  • 受保护的封装 protected
    • 受保护的封装是将对象成员进行一定级别的封装,然后在类中或者子类中都可以进行访问,但是在外部都不可以
    • 封装方法:在成员变量前面添加一个下划线即可
  • 公共的 public
    • 公共的成员实际上没有什么改变,任何地方都可以访问

继承

  • 继承就是一个类可以获得使用另外一个类中的成员属性和成员方法
  • 作用:减少代码,增加代码的复用功能,同时可以设置类与类直接的关系
  • 继承与被继承的概念
    • 被继承的类叫做父类,也叫做基类,也叫超类
    • 用于继承的类,叫做子类,也叫派生类
    • 继承与被继承之间有一个is-A关系
  • 继承的语法
# 在python中任何一个类都有一个父类object
class Person():
    name = "dana"
    age = 10
    def sleep(self):
        print("sleeping......")

# 父类写在括号中
class Teacher(Person):
    pass

t = Teacher()
print(t.name)
print(Teacher.name)

>>>
dana
dana
  • 继承的特征
    • 所有类都继承自object类,
    • 子类一旦继承父类,则可以使用除私有成员外的所有内容
    • 子类继承父类并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
    • 子类中可以定义独有的成员属性和方法
    • 子类中定义的成员和父类成员如果相同,优先使用子类中的
    • 子类里如果象扩充父类的方法,可以定义新方法的同时访问父类成员来进行代码重用,可以使用 父类名.父类成员 的格式来调用父类成员,也可以使用super().父类成员的格式来调用
class Person():
    name = "dana"
    __score = 0
    age = 10
    _petname = "sec"
    def sleep(self):
        print("sleeping......")
    def work(self):
        print("make some money")

class Teacher(Person):
    name = "yaona"
    def doTest(self):
        print("exam exam ...")
    def work(self):# 扩充父类的功能
        # 调用父类的功能,
        # Person.work(self)
        # 另一种方法
        super().work()
        self.doTest()

t = Teacher()
print(t.name)
t.doTest()
t.work()

>>>
yaona
exam exam ...
make some money
exam exam ...
  • 继承变量函数的查找顺序问题
    • 优先查找自己的变量
    • 没有则查找父类
    • 构造函数如果本类中没有定义,则自动查找调用父类的构造函数
    • 如果本类有定义。就不会往上查找
  • 构造函数
    • 是一类特殊的函数,在类进行实例化之前进行调用
    • 如果定义了构造函数,则实例化时使用构造函数,不查找父类的
    • 如果没有,则自动查找父类函数
    • 如果子类没定义,弗雷德构造函数带参数,则构造对象时的参数应该按父类参数构造
class Animel():
    def __init__(self):
        print("Animel ...")

class PaxingAni(Animel):
    def __init__(self, name):
        print("pa xing dong wu {}".format(name))

class Dog(PaxingAni):
    def __init__(self):
    	# PaxingAni.__init__(self) 这可以调用父类的构造函数
    	# super(PaxingAni, self).__init__() 同上
        print("wang wang ....")

class Cat(PaxingAni):
    pass

kaka = Dog() # 狗有自己的构造函数,就不往上查找构造函数
caca = Cat() # 猫没有自己的构造函数,则往上查找构造函数,这里报错,因为PaxingAni需要两个参数,实例化时只给里一个

>>>
wang wang ....
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-c5d20a992e9f> in <module>()
     15 
     16 kaka = Dog() 
---> 17 caca = Cat() 

TypeError: __init__() missing 1 required positional argument: 'name'
  • super()
    • super不是关键字,而是一个类
    • super的作用是获取MRO(MethodResolutionOrder)方法解析列表中的第一个类,往往就是指父类
    • super于父类直接没任何实质性关系,但通过super可以调用到父类
    • super使用的两个方法,在构造函数中调用父类的构造函数,
class A():
    pass

class B(A):
    pass

print(A.__mro__)
print(B.__mro__)

>>>
(<class '__main__.A'>, <class 'object'>)
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
单继承和多继承
  • 单继承:每个类只能继承一个类
  • 多继承:每个类能继承多个类
  • 优缺点
    • 单继承
      • 传承有序逻辑清晰,语法简单隐患少
      • 功能不能无限扩展,,只能在当前唯一的继承链中扩展
    • 多继承
      • 优点:类的功能扩展方便
      • 缺点:继承关系混乱
class Fish():
    def __init__(self, name):
        self.name = name
    def swim(self):
        print("swimming..........")

class Bird():
    def __init__(self, name):
        self.name = name
    def fly(self):
        print("flying.......")

class Person():
    def __init__(self, name):
        self.name = name
    def work(self):
        print("working......")

class SuperMan(Person, Bird, Fish):
    def __init__(self, name):
        self.name = name

s = SuperMan("yueyue")
s.work()
s.swim()
s.fly()

>>>
working......
swimming..........
flying.......
  • 菱形问题/钻石问题
    • 多个子类继承自同一个父类,这些子类又被同一个类继承,于是继承关系图成了一个菱形图谱
    • 关于多继承的MRO
      • MRO就是多继承中,用于保存继承顺序的一个列表
      • python本身采用C3算法来计算继承的菱形继承进行计算的结果
      • MRO计算原则:
        • 子类永远在父类前面
        • 如果多个父类,则根据继承原发中括号内类的书写顺序
        • 如果多个类继承自同一个父类,孙子类中只会选取继承语法括号中第一个父类的父类

多态

  • 多态就是同一个对象在不同情况下有不同的状态出现
  • 多态不是语法,是一种设计思想,不同于Java
  • 多态性:一种调用方式,不同的执行效果
  • 多态:同一事物的多种形态,动物分为人类,…
Mixin设计模式
  • 主要通过多继承方式,只是单一的增加功能,不改变质
  • 使用多继承语法实现Mixin
  • 使用Mixin实现多继承的时候非常小心
    • 首先它必须表示某一单一功能,而不是某个物品
    • 职责单一,如果有多个功能,则携程多个Mixin
    • Mixin不能依赖于子类的实现
    • 子类即使没有继承Mixin类,也能照样工作,只是缺少了某个功能
  • 优点
    • 使用Mixin可以在部队类进行任何修改的情况下,扩充功能
    • 可以方便的组织和维护不同功能组件的划分
    • 可以根据需要任意调整功能类的组合
    • 可以避免创建很多新的类,导致类的继承混乱
class Person():
    name = "dana"
    age = 18
    def eat(self):
        print("Eatting.......")
    def drink(self):
        print("Drinking......")
    def sleep(self):
        print("Sleeping.......")
class Teacher(Person):
    def work(self):
        print("Working......")
class Student(Person):
    def study(self):
        print("Studying......")
class Tutor(Teacher, Student):
    pass
t = Tutor()
print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)

class TeacherMixin(): # Mixin不需要继承父类,只是一个功能的拓展
    def work(self):
        print("Working......")
class StudentMixin():
    def study(self):
        print("Studying......")
class TutorM(Person, TeacherMixin, StudentMixin):
    pass

tt = TutorM()
print(TutorM.__mro__)
print(tt.__dict__)
print(TutorM.__dict__)

>>>
(<class '__main__.Tutor'>, <class '__main__.Teacher'>, <class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
(<class '__main__.TutorM'>, <class '__main__.Person'>, <class '__main__.TeacherMixin'>, <class '__main__.StudentMixin'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
类相关函数
  • issubclass:检测一个类是不是另一个类的子类
  • isinstance:检测一个对象是不是一个类的实例
  • hasattr:检测一个实例是否有成员xxx
  • getattr
  • setattr
  • delattr
  • dir:获取对象成员的列表
class A():
    name = "noName"
class B(A):
    pass
class C():
    pass

print(issubclass(B, A))
print(issubclass(C, A))
a = A()
print(isinstance(a, A))
print(hasattr(a, "name"))
print(hasattr(a, "age"))

>>>
True
False
True
True
False
类的成员描述符
  • 类的成员描述符是为了在类中对类的成员属性进行相关操作而创建的一种方式
    • get:获取属性操作
    • set:设置属性操作
    • delete:删除属性操作
  • 如果象使用类的成员描述符,大概有三种方法
    • 使用类实现描述器
    • 使用属性修饰符
    • 使用property函数
      • property函数很简单
      • property(fget, fset, fdel, doc)
class Person():
    def fget(self):
        return self._name
    def fset(self,name):
        self._name = name.upper()
    def fdel(self):
        self._name = "NoName"
    name = property(fget, fset, fdel, "对name进行操作!")

p = Person()
p.name = "dana"
print(p.name)

>>>
DANA
  • 无论哪种修饰符都是为了队成员属性进行相应的控制
    • 类的方式:适合多个类中的多个属性共用一个描述符
    • property:使用当前类中使用,可以控制一个类中多个属性
    • 属性修饰符:适用于当前类中,控制一个类中一个属性
类的内置属性

__dict__:以字典的方式显示类的成员属性
__doc__:显示类的说明文档
__name__:获取类的名称
__bases__:显示类的所有父类

类的常用魔术方法

  • 魔术方法就是不需要人为调用的方法,基本实在特定时刻自动触发
  • 魔术方法的统一特征,方法名被前后个两个下划线包裹
  • 操作类
    • __init__:构造函数
    • __new__:对象实例化方法,此函数特殊,一般不需要使用
    • __call__:对象当函数时触发
    • __str__:当对象被当作字符串时调用
    • __repr__:返回字符串,跟__str__有一点区别
class A():
    def __init__(self):
        print("我被调用了") 
    def __call__(self):
        print("我有被调用了again")
    def __str__(self):
        return "我是字符串"

a = A()
a()
print(a)

>>>
我被调用了
我有被调用了again
我是字符串
  • 描述符相关
    • __set__
    • __get__
    • __delete__
  • 属性操作相关
    • __getattr__:访问一个不存在的属性时触发
    • __setattr__:队成员属性进行修改时触发
    • 参数
      • self,又来获取当前对象
      • 被设置的属性名称,以字符串形式出现
      • 需要对属性名称设置的值
    • 作用:进行属性设置的时候进行验证或者修改
    • 注意:在该方法中不能对属性直接进行赋值操作,或者死循环
class Person():
    def __init__(self):
        pass
    def __setattr__(self, name, value):
        print("设置属性:{0}".format(name))
        # self.name = value 这种会直接死循环
        super().__setattr__(name,value) # 为了避免死循环,规定统一调用父类魔法函数
p = Person()
print(p.__dict__)
p.age = 19

>>>
{}
设置属性:age
  • 运算相关的魔法函数
    • __gt__:进行大于判断时候触发的函数
    • 参数
      • self,
      • 第二个参数是第二个对象
      • 返回值可以是任意值,推荐布尔值

类和对象的三种方法

  • 实例方法
    • 需要实例化之后才能用的方法
  • 静态方法
    • 不需要数理化,通过类直接使用
  • 类方法
    • 不需要实例化
  • 区别百度
class Person():
    # 实例方法
    def eat(self):
        print(self)
        print("eating.....")
    # 类方法
    # 类方法的第一个参数,一般命名为cls,区别于self
    @classmethod
    def play(cls):
        print(cls)
        print("playing.......")
    # 静态方法
    # 不需要用第一个参数表示自身或者类
    @staticmethod
    def say():
        print("saying....")

yueyue = Person()
# 实例方法
yueyue.eat()
# 类方法
Person.play()
yueyue.play()
# 静态方法
Person.say()
yueyue.say()

>>>
<__main__.Person object at 0x00000210C2FD2B00>
eating.....
<class '__main__.Person'>
playing.......
<class '__main__.Person'>
playing.......
saying....
saying....

抽象类

  • 抽象方法:没有具体实现内容的方法成为抽象方法
  • 抽象方法的主要意义是规范了子类的行为华人接口
  • 抽象类的使用需要借助abc模块
  • 抽象类:包含抽象方法的类叫抽象类,
  • 抽象类的使用
    • 抽象类可以有抽象方法,也可以有具体的方法
    • 抽象类中可以有方法也可以有属性
    • 抽象类不能实例化
    • 必须继承才可以使用,且继承的子类必须实现所有继承来的抽象方法
    • 假定子类没有实现所有继承的抽象方法,则子类也不能实例化
    • 抽象类的主要作用是设定类的标准,以便于开发的时候具有统一的规范
# 抽象类的实现
import abc
# 声明一个类并且指定当前类的元类
class Human(metaclass = abc.ABCMeta):
    # 定义一个抽象方法
    @abc.abstractmethod
    def smoking(self):
        pass
    # 定义类抽象方法
    @abc.abstractclassmethod
    def drink():
        pass
    # 定义静态抽象方法
    @abc.abstractstaticmethod
    def play():
        pass

自定义类

  • 类其实是一个普通类的定义和一些方法的自由组合
  • 可以定义类和函数,然后自己通过类直接赋值
class A():
    pass
def say(self):
    print("Saying ... ... ..")
class B():
    def say(self):
        print("saying.........")

say(9)
A.say = say
a = A()
a.say()

>>>
Saying ... ... ..
Saying ... ... ..
  • 可以借助于MethodType实现
from types import MethodType
class A():
    pass
def say(self):
    print("Saying ... ... ..")

a = A() 
# a.say = say 实例不能和方法直接组装,必须如下
a.say = MethodType(say, A)
a.say()

>>>
Saying ... ... ..
  • 可以借助于type实现
# type造类
def say(self):
    print("saying.... ... ..")
def talk(self):
    print("talking.... ... ..")

A = type("AName", (object,), {"class_say":say, "class_talk":talk})
a = A()
a.class_say()
a.class_talk()

>>>
saying.... ... ..
talking.... ... ..
  • 可以借助元类实现 MetaClass
    • 元类是类
# 元类的写法固定,必须继承自type
class TMethodClass(type):
    def __new__(cls,name,bases,attrs):
        print("hahah,我是元类")
        attrs['id'] = '000000'
        attrs['addr'] = 'shiyanshi'
        return type.__new__(cls, name, bases, attrs)
# 元类定义完就可以使用,使用注意写法
class Teacher(object, metaclass=TMethodClass):
    pass

t = Teacher()
print(t.id)

>>>
hahah,我是元类
000000

anaconda基本使用

  • anaconda主要是一个虚拟环境管理器
  • 还是一个安装包管理器
  • conda list:显示anaconda安装的包
  • conda env list:显示anaconda的虚拟环境列表
  • conda create -n xxx python=3.6:创建python3.6版本的虚拟环境,名称为xxx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值