面向对象

编程范式

面向对象指的是一种编程范式;

编程范式:可以理解为一种编程方式,编程规范,编程模式

  • 面向过程

面向过程编程:是一种以过程为核心的编程,主要是在解决一个问题的时候 , 将这个问题拆分成一个个小步骤完成并且拼接起来的就是面向过程。

  • 面向对象

面向对象编程(object oriented programming ,简称OOP):是一种以对象为核心的编程 , 面向对象在解决问题的时候是思考如何设计这个事情。

类和对象

  • 在代码里面实行一个对象是通过类进行产生的;

  • 类就相当于生活中的一个类别,一个比较抽象的概念。

  • 对象就是一个通过类产生的一个拥有属性以及行为的实体,是对应类里面的一个个体

  • <先有类 , 再有对象>

  1. 函数是一系列的指令,完成特定的功能。变量是描述事物特征的东西,姑且叫信息。
  2. 类就是将变量信息和函数指令结合到一起的东西。变量表达类的特征(属性),函数表示类的功能(方法)。
  3. 对象是类的特例。犹如动物和人的关系。

类的定义

python中如何定义一个函数

# 定义
def 函数名(形参):
    函数体
# 调用
函数名(实参)

def func_1(name):
    return f'{name}是个超人'

print(func_1('伤心超人'))

python中定义类的语法:

class 类名:
   代码

类目的定义:使用驼峰式命名(大驼峰,每个单词的首字母大写)

定义类之后 , 执行代码 , 类里面的代码也会一起执行

定义再类中的变量称之为属性

class People:
    '''
    我是类文档
    就相当于类的说明书
    '''
    # 类属性  (所有对象共有的)
    height = 1.73
    weight = 200
    
# 实例化类
xs = People()
print(xs.weight)
# print(People.__dict__)

mj = People()
# 创建对象独有的属性,如果属性存在,则是修改属性值的操作
# 对象名.属性名 = 属性值
mj.weight = 90
mj.weight = 85
print(mj.weight)
# 使用对象.__dict__得到的是对象的属性
print(mj.__dict__)

# 修改类属性
# 类名.属性名 = 属性值
People.weight = 170
print(xs.weight)

# 动态的增加类属性
People.age = 20
print(xs.age)

--init--

class Animal:
    '''动物类'''
    # 初始化方法
    def __init__(self , name , age):
        # 属性
        self.age = age
        self.name = name
        
cat = Animal('da' , 1)
print(cat.name)
print(cat.age)

dog = Animal('xi',1.5)
print(dog.name)

self

class Animal:
    '''动物类'''

    # 初始化方法
    def __init__(self , name , age):
        # 属性
        self.age = age
        self.name = name

    def sleep(self):
        print(f'{self.name}睡觉')

cat = Animal('da' , 1)
dog = Animal('xi',1.5)
dog.sleep()
# print(cat.name)
# print(cat.age)
# print(dog.name)

封装

面向对象的三大特性:封装、继承、多态

  • ​ 封装:把功能代码 , 数据封装到某一个地方,需要的时候进行调用 , 提过程序的安全性。即把属性以及方法放到类中 ,通过类对象的操作或者调用。

  • ​ 通过封装,可以将一些不想给用户看到的功能进行隐藏 , 用户只能访问公开的功能,可以提高数据的安全性 , 也对后期的维护降低成本。

  • ​ 封装的好处:便于分工 , 便于复用 , 可扩展性强。

class People:
    '''人类'''
	height = 1.73
    weight = 200
    # 初始化方法
    def __init__(self , name , age , weight , job):
        self.name = name
        self.age = age
        self.weight = weight
        self.job = job

    def eat(self):
        print(f'{self.name}食べる')
        self.weight += 2

    def drink(self):
        print(f'{self.name} 飲みましょう。')
        self.weight += 0.5

    def run(self):
        print(f'{self.name} gogogo')
        self.weight -= 0.5


属性查找

  • 在实例化属性中不同的对象 , 调用的属性不会产生影响互不干扰

  • 对象寻找对应的属性,先从自身的实例属性开始查找 , 如果没有再从类属性查找。

  • 不同的对象之间的实例属性是互不相通 , 都是独有的 , 两者无法相互访问

class Cat:
    # 类属性
    category = 'ねこ'
    age = 2
    # 初始化方法
    def __init__(self , name , category):
        # 实例属性
        self.name = name
        self.category = category

    def sleep(self):
        print(f'{self.name}睡觉')

mao1 = Cat('xiaopang' , '家猫')
mao1.age = 1
print(mao1.name)
print(mao1.category)

mao2 = Cat('dapang','流浪猫')
print(mao2.name)
print(mao2.age)	# 2
print(mao2.category)

属性隐藏

在类中定义一些数据或者功能不希望被外部访问到以及操作的时候 , 可以将数据以及功能进行隐藏。

在Python中没有绝对的隐藏 , 是可以通过一些方法对其进行访问的。

在类中的三种隐藏方法:

  • 属性或者方法名前加上双下划线
  • 使用property()函数
  • @property 装饰的方法是获取属性值的方法 , 被装饰的方法的名字会被作为属性名 , 方法名尽量跟隐藏的属性名一致
    @属性名.setter 装饰的方法是设置属性值的方法
    @属性名.deleter 装饰的方法是删除属性值的方法

属性或者方法名前加上双下划线

双下划线开头的属性 , 是对象的隐藏属性,隐藏属性可以在类内部进行访问

class People:

    def __init__(self , name , age):
        self.name = name
        self.__age = age    # 将age属性进行隐藏

    def __speak(self):
        print(f'我今年{self.__age}')

hh = People('haha', 24)
# print(hh.name)
# 获取对象属性
# print(hh.__dict__)
# 对象名称._类名__属性名
print(hh._People__age)
hh._People__speak()

使用property()函数

property可以设置、获取 , 删除对象的某一个属性值 , 也可以限制用户对属性的设置与获取

# property 属性
# property(fget = None , fset = None , fdel = None , doc = None)
# fget = 是获取属性值的方法
# fset = 是设置属性值的方法
# fdel = 是删除属性值的方式

class People:

    def __init__(self , name , age):
        self.name = name
        self.__age = age    # 将age属性进行隐藏

    def speak(self):
        print(f'我今年{self.__age}')

    def get_age(self):
        print('===get=====')
        return self.__age

    def set_age(self , value):
        print('===set=====')
        self.__age = value

    def del_age(self):
        print('===del=====')
        del self.__age

    # 赋予对象属性权限 加上这句话,可以限制权限,而且对象进行操作很符合习惯
    age = property(fget=get_age , fset=set_age , fdel=del_age)

p2 = People('小小怪', 26)
print(p2.name)
# 获取对象属性
print(p2.__dict__)
# 对象名称._类名__属性名
print(p2._People__age)
# ac._People__speak() # 'People' object has no attribute '_People__speak'
# 因为本来就没有__speak,如果没有property,speak可以直接调用
print(p2.age)
p2.age = 22
print(p2.age)
del p2.age # 如果没有property,删除就得是p2.del_age,和习惯不同
print(p2.__dict__)

装饰器

class Student:
    def __init__(self):
        self.__name = '开心超人'

    @property  # grade = property(grade) 不清楚具体咋实现的,但是结果是 将函数的返回值 __name 赋给grade,也就是下变函数的名字,所以说为了让外边调用方便还是不要瞎起名
    def grade(self):
        return self.__name

    @grade.setter
    def grade(self , value):
        self.__name = value

    @grade.deleter
    def grade(self):
        del self.__name


p5 = Student()
print(p5.grade)
p5.grade = "花心超人"
print(p5.grade)
del p5.grade
# print(p5.grade)

继承

  1. 在python中是在多个类中的一种所属关系
  2. 被继承的类称为父类
  3. 接收的类称为子类
  4. 在继承中子类是默认继承父类中的属性以及方法
  5. 继承可以解决类与类之间的代码冗余。
  6. 在python3中都会默认继承object类 , 即object是python中所有类的父类(基类 , 超类)。

单继承

一个子类继承一个父类 , 子类默认继承父类中的所有方法 , 属性

class Father:

    def __init__(self , name):
        self.name = name
        self.clothes = '👚'
        self.job = '工作'

    def eat(self):
        print('吃')
        return '吃东西'

class Son(Father):
    pass

p3 = Son('校长')
print(p3.name)
print(p3.clothes)
print(p3.job)
print(p3.eat())

重写

在子类中重新定义父类的方法 , 子类在实例化之后会默认的访问自身的方法。

class Father:

    def __init__(self , age , name='孙悟空'):
        self.name = name
        self.age = age
    def eat(self):
        print(f'{self.name}偷吃亚奇罗贝 的 <・)))><<')
    def money(self):
        print('1万')
        
class GrilOfSon(Father):
    def eat(self):
        print(f'{self.name}在优雅的吃饭')
    # super()
    def money(self):
        print('5千')
        # 方法一,指出父类名 需要self
        Father.money(self)
        # 方法二 , 通过super() 不用self
        super().money()

p4 = GrilOfSon('小芳' , 8)
p4.eat()
p4.money()

属性查找:子类对象自身——>子类中——>父类中

多层继承

被继承类都有属于自己的父类

class A:
    name = '1A'
    name1 = '11A'

class B(A):
    name = '2B'
    name2 = '22B'

class C(B):

    name = '3C'
    name3 = '33C'

    def __init__(self):
        self.name = '百事通'

q = C()
print(q.name)
# 如果没有初始化就会查到C类的name;
# 如果C类没有name,就会查到B类的name;以此类推

# 可以将前辈的东西都继承了,人要是天生就继承就好了
print(q.name1)
print(q.name2)
print(q.name3)
# 只能查到初始化方法里的属性
print(q.__dict__)

多继承

多继承:一个子类有多个父类

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法

class Horse:
    def body(self):
        return '健壮'

class Donkey:
    def body(self):
        return  '娇小'
    def labour(self):
        return  '勤快能干'

class Mule(Horse , Donkey):
    pass

M = Mule()
print(M.body()) # 健壮
print(M.labour()) # 勤快能干
# 查看继承的顺序
print(Mule.__mro__)
# 查看第一个父类
# print(Mule.__base__)
# 在多继承中查看所有的父类,查不到爷爷类
print(Mule.__bases__)

# (<class '__main__.Mule'>, <class '__main__.Horse'>, <class '__main__.Donkey'>, <class 'object'>)
# <class '__main__.Horse'>
# (<class '__main__.Horse'>, <class '__main__.Donkey'>)

多态

不同的对象, 调用同一个方法 , 表现出不同的形态。

多态的实现:1、必须要有类的继承;2、子类对父类的方法进行重写

class A:
    def func(self):
        print('狗叫')

class B(A):
    def func(self):
        print('猫叫')

a = A()
b = B()
a.func()
b.func()

检查类型

# type()    # 检查单个的数据类型
# issubclass(cls , class_tuple)    # 检查类是否为后者的子类(检查前者是否继承后者)
# isinstance(obj , cls)    # 检查   对象   是否为类中的

res = 'jjjj'

print(isinstance(res , int)) # False

print(issubclass(str, object)) # True

class Father:
    pass

h = Father()
print(isinstance(h, Father)) # True
print(isinstance(res, Father)) # False

class Son(Father):
    pass

print(issubclass(Father, Son)) # False
print(issubclass(Son, Father)) # True

_str_

str 是一个类的方法,在打印类对象,获取其属性信息时调用。

打印一个实例化对象时,默认打印的其实时一个对象的地址,

但是我们可以对其进行重载,打印我们想要的信息。

class people:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '这个人的名字是%s,已经有%d岁了!'%(self.name,self.age)

a=people('孙悟空',999)
print(a)

# 这个人的名字是孙悟空,已经有999岁了!
# 如果没有重载函数的话输出的就是一串看不懂的字符串:
# <__main__.people object at 0x00000272A730D278

_del_

当检测到对象没有在继续引用时 , 就会自动的将对象所占用的内存空间清除

class Person:

    def __init__(self, name):
        self.name = name


    def __str__(self):
        return f'{self.name}来了 '


    def __del__(self):
        print(f'{self.name}被清除')


cx = Person('粗心超人')
tx = Person('甜心超人')
print(cx)
del cx

print('=' * 20)
print(tx)
print('kaijiu')

绑定与非绑定

类方法(绑定)

  • 通过@classmethod进行方法装饰
  • 类方法操作的是类属性 , 同cls进行对类的绑定
  • 类方法和对象方法(实例方法)都是绑定的
# 类方法(绑定)

class Student:

    # 类属性
    number = 0

    # 实例属性
    def __init__(self , name):
        self.name = name
        # 实例化的时候会执行下边的count函数,数量加一,
        # 每次创建对象就会被监测到,而且给他编个号
        self.id = self.count()

    @classmethod
    def count(cls):
        cls.number += 1
        return cls.number

ddg = Student('大大怪')
print(ddg.number)
xxg = Student('小小怪')
print(ddg.number)
print(Student.number)
print(ddg.id)
print(xxg.id)

静态方法(非绑定)

  • 通过@staticmethod进行方法装饰
  • 不需要绑定self以及call
  • 静态方法与定义在类外面的函数是一致的
  • 只是放在类中 ,方便管理(维护)。
import time
class FanPai:
    def __init__(self, name):
        self.name = name

    @staticmethod
    def str_time():
        print(f'{time.strftime("%Y/%m/%d")}')

ddg = FanPai('大大怪')
xxg = FanPai('小小怪')
# 类和对象都可以 调用
# 类里边的的静态方法类似于模块里的函数
ddg.str_time()
xxg.str_time()
FanPai.str_time()