python面向对象

183 阅读14分钟

面向对象

编程范式

面向过程编程
面向过程:核心是过程二字,过程指的是解决问题的步骤
        优点:复杂的问题流程化,进而简单化
        缺点:可扩展性差
面向对象编程
面向对象:核心是对象二字,对象就是特征与技能的结合体
		优点:可扩展性强
		缺点:编程复杂度高
类:类就是一系列对象相似的特征与技能的结合体
类与对象的区别
对象是类的实例化
类是相同结构的对象的抽象

面向对象的三大特点

1、封装,在类中数据的赋值、内部调用对外部用户是透明的
2、继承,一个类可以派生出子类,子类自动添加父类的属性和方法
3、多态,一个接口,多种实现

定义类和实例化出对象

# 先定义类
class LuffStudent:
    shcool = 'luffcity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is eating')

    def sleep(self):
        print('is sleep')


# 后定义对象
stu1 = LuffStudent()  # 实例化一个对象
stu2 = LuffStudent()
stu3 = LuffStudent()
print(stu1)
print(stu2)
print(stu3) # 返回一个对象
类的使用
class LuffStudent:
    school = 'luffcity'

    def learn(self):
        print('is learning')

    def eat(self):
        print('is eating')


# 查看类的命名空间 返回一个字典
print(LuffStudent.__dict__)

# 查
# print(LuffStudent.school)

# 增
LuffStudent.county = 'chaina'
print(LuffStudent.county)

# 删
del LuffStudent.county

# 改
LuffStudent.school = 'Luffcity'
print(LuffStudent.school)
对象的使用
# __init__ 方法用来为对象定制对象自己独有的特征
class LuffStudent:
    school = 'luffcity'

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

    def learn(self):
        print('is learning')

    def eat(self):
        print('is eating')

# 产生对象
stu1 = LuffStudent('nida','M',18)
print(stu1.__dict__)
# 加上__init__方法后,实例化的步骤
'''
    1、先产生一个空对象
    2、调用LuffStudent.__init__(stu1,'lida','M',18)
'''
# 查属性
# print(stu1.__dict__)

# 改属性
# stu1.name = 'tom'
# print(stu1.name)

# 删属性
# del stu1.sex
# print(stu1.__dict__)

# 增属性
# stu1.class_name = 'Python'
# print(stu1.class_name)
属性查找与绑定方法
class LuffStudent:
    school = 'luffcity'

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

    def learn(self):
        print('%s is learning' % (self.name))

    def eat(self):
        print('%s is eating' % (self.name))


stu1 = LuffStudent('王二丫', '女', 18)
stu2 = LuffStudent('李三泡', '男', 38)
stu3 = LuffStudent('张铁蛋', '男', 48)

# 对象:特征与技能的结合体
# 类:类是一系列对象相似的特征与相似的技能的结合体
'''
    类中的数据属性:所有对象共有的
        print(LuffStudent.school,id(LuffStudent.school))
        print(stu1.school,id(stu1.school))
        print(stu2.school,id(stu2.school))
        print(stu3.school,id(stu3.school))
    他们的命名空间都指定到了一个命名空间中
'''

# 类中的函数属性:是绑定给对象的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,会把对象本身当作第一个参数传入,传给self
print(LuffStudent.learn)
print(stu1.learn)
print(stu2.learn)
print(stu3.learn)
继承(什么是什么的关系)
'''
    继承,是指类与类之间的关系
'''


class Parentclass1():
    pass


class Parentclass2():
    pass


class SubClass1(Parentclass1):
    pass


class SubClass2(Parentclass1, Parentclass2):
    pass


# print(SubClass1.__bases__)  # 查看继承了那些父类
# print(SubClass2.__bases__)


'''
    属性查找小练习
'''

class Foo:
    def f1(self):
        print('from Foo.f1')

    def f2(self):
        print('from Foo.f2')
        self.f1()


class Bar(Foo):
    def f1(self):
        print('from Bar.f2')


b = Bar()
b.f2()  # 这个打印的b.f1()
print(Bar.mro())  # 打印继承的执行的顺序
继承的实现原理
# python2.* 新式类,经典类
# python3.* 新式类

# 在python2中——》经典类:没有继承object的类型,以及它的子类都称为经典类
'''
class Foo:
    pass


class Bar(Foo):
    pass
'''

# 在python3中——》新式类:继承object的类型,以及它的子类都称为新式类
# 在python3--> 新式类,一个类没有继承object类,默认就继承object
'''
class Foo(object):
    pass


class Bar(Foo):
    pass
'''

'''
    经典类: 深度优先查找,从左到右,一查到底
    新式类: 广度优先查找,平行查找
'''

在子类中重用父类的方法或属性
# 在子类派生出的新的方法中重用父类的方法,有两种实现方式
# 方式一、指名道姓 不依赖继承
# 方式二、 super() 依赖继承 
class Hero(object):
    def __init__(self, nickname, life_value, aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity

    def attack(self, enemy):
        enemy.life_value -= self.aggresivity


class Garen(Hero):
    camp = 'Demacia'

    def __init__(self, nickname, life_value, aggresivity, weapon):
        super().__init__(nickname, life_value, aggresivity)  # super() python3中可以不用写类和self
        self.weapon = weapon

    def attack(self, enemy):
        # Hero.attack(self, enemy)  # 指名道姓方法 不依赖继承方法
        super(Garen,self).attack(enemy) # 依赖继承
        print('form Garen class')


g = Garen('草丛轮', 100, 30, '大宝剑')
print(g.__dict__)
组合(什么有什么的关系)
class People:
    school = 'luffycity'

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


class Teacher(People):

    def __init__(self, name, age, sex, level, salary):
        super().__init__(name, age, sex)
        self.level = level
        self.salary = salary

    def teach(self):
        print('%s is teaching' % self.name)


class Course():
    def __init__(self, course_name, course_price, course_period):
        self.course_name = course_name
        self.course_price = course_price
        self.course_period = course_period

    def tell_info(self):
        print('课程名<%s> 课程价钱<%s> 课程周期<%s>' % (self.course_name, self.course_price, self.course_period))


class Student(People):

    def __init__(self, name, age, sex, class_time):
        super().__init__(name, age, sex)

        self.class_time = class_time

    def learn(self):
        print('%s is lenrning' % self.name)


class Date:
    def __init__(self, year, mon, day):
        self.year = year
        self.mon = mon
        self.day = day

    def tell_info(self):
        print('%s-%s-%s' % (self.year, self.mon, self.day))


python = Course('python', 3000, '3moms')
linux = Course('linux', 2000, '4mons')

teacher1 = Teacher('alex', 18, 'male', 10, 3000)
teacher1.course = python

student1 = Student('张三', 28, 'female', '08:30:00')
d = Date(1988, 4, 20)
student1.birh = d
student1.birh.tell_info()

# student1.course1 = python
# student1.course2 = linux

# student1.course1.tell_info()
# student1.course2.tell_info()

# student1.courses = []
# student1.courses.append(linux)
# student1.courses.append(python)
# student1.courses[1].tell_info()
抽象类
import abc

'''
    abc模块实现一个抽象类的规范
'''


class Animal(metaclass=abc.ABCMeta):  # 这是一个抽象类 这个抽象类只能被继承不能实例化
    @abc.abstractmethod
    def run(self):
        pass

    @abc.abstractmethod
    def eat(self):
        pass
# animal = Animal()

class People(Animal):
    def run(self):
        print('people is walking')

    def eat(self):
        print('people is eating')


class Pig(Animal):
    def run(self):
        print('pig is walking')

    def eat(self):
        print('pig is eating')


class Dog(Animal):
    def run(self):
        print('dog is walking')

    def eat(self):
        print('dog is eating')


peo1 = People()
pig1 = Pig()
dog1 = Dog()
peo1.run()

多态
'''
    多态:同一类型事物的多种形态
'''

import abc


class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass


class People(Animal):  # 动物的形态之一:人
    def talk(self):
        print('say hello')


class Dog(Animal):  # 动物的形态之二:狗
    def talk(self):
        print('say wangwang')


class Pig(Animal):  # 动物的形态之三:猪
    def talk(self):
        print('say aoao')


# 多态性:指定是可以在不考虑对象的类型的情况下1而直接使用对象

peo1 = People()
dog1 = Dog()
pig1 = Pig()


# 增加了程序的灵活性
#   以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

def func(animal):
    animal.talk()


func(peo1)


# 增加了程序额可扩展性
#   通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用 &emsp;&emsp;


# 鸭子类型
# Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
# 二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass


class DiskFile:
    def read(self):
        pass

    def write(self):
        pass

封装之如何隐藏属性
class A:
    __x = 1

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

    def __foo(self):
        print('run foo')

    def bar(self):
        self.__foo()
        print('from bar')


# print(A.__dict__)  # 可以看出 __x这个变量进行了一个变形 变成了_A__x
# a= A('alex')
# a.bar()
'''
这种变形的特点:
    1、在类外部无法直接obj.__AttrName
    2、在类内部是可以最近使用:obj.AttrName
    3、 子类无法覆盖父类__开头的属性
'''


class Foo():
    def func(self):
        print('from foo')


class Bar(Foo):
    def func(self):
        print('from bar')


# b = Bar()
# b.func()
'''
    总结这种变形需要注意的问题:
        1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
        2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
        3、变形这个过程是在内定义的时候就已经发生了
        4、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的      
'''


class B:
    __x = 1  # _B__x = 1

    def __init__(self, name):
        self.__name = name  # _B__name


# 验证问题1
# print(B._B__x)

# 验证问题2,3
# B.__y = 2
# print(B.__y) # 内定义结束之后就没有隐藏属性的这个说法了

# 验证问题4
class A():
    def __foo(self):
        print('A.foo')

    def bar(self):
        print('A.bar')
        self.__foo()  # 这个打印的是_A__foo()
        self.foo()  # 这个打印的是B.foo()


class B(A):
    def foo(self):
        print('B.foo')


b = B()
b.bar()

封装的意义
'''
1、封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为

'''


class Peoplee:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def tell_info(self):
        print('Name:<%s> Age:<%s>' % (self.__name, self.__age))

    def set_info(self, name, age):
        if not isinstance(name, str):
            print('名字必须是字符类型')
            return
        if not isinstance(age, int):
            print('名字必须是数字类型')
            return

        self.__name = name
        self.__age = age


# p = Peoplee('egon', 18)
# p.set_info('EGON',28)
# p.tell_info()

# 封装方法: 隔离复杂度,

class ATM:
    def __card(self):
        print('插卡')

    def __auth(self):
        print('用户认证')

    def __input(self):
        print('输入取款金额')

    def __print_bill(self):
        print('打印账单')

    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


a = ATM()
a.withdraw() # 用户直接访问即可 不用管ATM的工作流程
封装与可扩展性
class Room:
    def __init__(self, name, owner, weight, length,height):
        self.name = name
        self.owner = owner

        self.__weight = weight
        self.__length = length
        self.__height = height

    def tell_area(self):
        return self.__weight * self.__length * self.__height


r = Room('卫生间', 'alex', 10, 10,10)
print(r.tell_area())    # 用户只需要访问接口即可 不管我内部的变量怎么变化
property的使用
'''
BMI指数 (bmi是计算而来的)
算法
体制指数(BMI)=体重(kg)+身高^2(m)
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常胖,高于32
EX: 70kg/(1.75*1.75) = 22.86
'''


class People:
    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.weight = weight

    @property # 一个伪装装饰器 可以将一个方法伪装成数据类型返回,但是本质上还是一个方法
    def bmi(self):
        return self.weight / (self.height ** 2)

p = People('eaon', 1.81, 75)
print(p.bmi)
property的针对使用
class People:
    def __init__(self, name):
        self.__name = name

    @property  # 针对属性的查看行为 
    def name(self):
        return self.__name

    @name.setter  # 针对属性的修改行为 setter是关键字
    def name(self, val):
        if not isinstance(val, str):
            print("名字必须是个字符串")
            return
        self.__name = val

    @name.deleter  # 针对属性的删除行为 是关键字deleter
    def name(self):
        print('不允许输出')


p = People('egon')
print(p.name)  # 查看

p.name = 'EGON'  # 修改
print(p.name)
del p.name  # 删除
绑定方法与非绑定方法介绍
'''
在类内部定义的函数,分为两大类:
    1、绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入

        绑定到对象的方法:在类内定义的没有被任何装饰器修饰的方法

        绑定到类的方法: 在类内定义的被classmethod装饰器修饰的方法

    2、非绑定方法:没有自动传值的说法,就是在类中定义了应该普通的员工方法工具,类也可以实现
        非绑定方法:不与类或者对象绑定,被staticmethod装饰器修饰的

'''

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

    def tell(self):	# 没有被任何装饰器修饰的就是绑定到对象的放到
        print('名字是%s' % self.name)

    @classmethod	# 被classmethod修饰过的就是绑定到类的方法
    def func(cls):  # cls=Foo
        print(cls)

    @staticmethod 
    def func1(x, y):
        print(x + y)


f = Foo('egon')
# f.tell()
# print(Foo.func)
# print(Foo.func1)
# print(f.func1)
Foo.func1(1,2)
f.func1(3,4)
绑定方法与非绑定方法的使用
import settings
import hashlib
import time

class People:
    def __init__(self, name, age, sex):
        self.id = self.create_id()
        self.name = name
        self.age = age
        self.sex = sex

    def tell_info(self):  # 绑定到对象的方法
        print('Name:%s Age:%s Sex:%s' % (self.name, self.age, self.sex))

    @classmethod
    def from_cinf(cls):
        obj = cls(
            settings.name,
            settings.age,
            settings.sex,
        )
        return obj

    @staticmethod
    def create_id():
        m=hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()
# p = People('egon', 18, 'male')

# 绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入
# p.tell_info()

# 绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入
# p = People.from_cinf()  # from_cinf(People)
# p.tell_info()

# 非绑定方法,不与类或者对象绑定,没有自动传值怎么一说
p1 = People('alex',18,'M')
p2 = People('Alex',28,'M')
p3 = People('clex',38,'M')
print(p1.id)
print(p2.id)
print(p3.id)
反射
# 反射;通过字符串映射到对象的属性

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

    def talk(self):
        print('%s is talking' % self.name)


obj = People('egon', 18)

choice = 'name'
# print(obj.choice)  # 字符串形式无法调用类型

hasattr(obj, choice)  # 可以通过这个内置方法查看这个字符串在类字典中是否存在 obj.__dict__中
print(getattr(obj,choice,None)) # None的作用当不存在这个属性时返回应该None

setattr(obj,choice,'male') # 通过字符串的方式修改属性
print(obj.name)

delattr(obj,choice) # 通过字符串的方式删除属性
print(obj.__dict__)
反射的应用
class Service:
    def run(self):
        while True:
            inp = input('>>>:').strip() # 当和用户交互时input返回的是一个字符串类型
            cmds = inp.split()
            if hasattr(self, cmds[0]): # 判断用户输入的cmd有没有这个方法
                func = getattr(self, cmds[0]) # 传入方法
                func(cmds)

    def get(self,cmds):
        del cmds[0]
        print('get....',cmds)

    def pull(self,cmds):
        del cmds[0]
        print('pull...',cmds)


obj = Service()
obj.run()

内置方法isinstance() 和 issubclass()
# isinstance() # 检查是否obj是否是类 cls 的对象
class Foo(object):
    pass


obj = Foo()
print(isinstance(obj, Foo)) # 存在返回一个True


# issubclass() # 检查sub类是否是 super 类的派生类
class Foo1(object):
    pass


class Bar(Foo1):
    pass


print(issubclass(Bar, Foo1)) # 存在返回一个True

内置方法item系列 将对象模拟成一个字典,进行操作

# item系列 ,将对象模拟成一个字典,进行操作
class Foo():
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):  # 查
        return self.__dict__.get(item)


    def __setitem__(self, key, value):  # 改
         self.__dict__[key]=value

    def __delitem__(self, key):  # 删
        del self.__dict__[key]


obj = Foo('egon')

# print(obj['name']) # 查看属性

obj['sex'] = 'male' # 设置属性
print(obj.__dict__)

del obj['sex']
print(obj.__dict__)

内置方法str

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

    def __str__(self):
        return '<name:%s,age:%s>' % (self.name, self.age) # 必须是一个字符串


obj = People('egon', 18)
print(obj)  # obj.__str__()
# __str__ 定义完后或者打印的时候触发,并且以返回值的方式返回,返回的对象必须是一个字符串

内置方法del

# __del__ 在程序结束后自动回收系统资源,如果不加python不会回收系统资源
# del 可以手动回收系统资源
# python 自动回收机制,只会回收python程序中的资源,不会回收操作系统支援

class Open:
    def __init__(self, filename):
        print('Open file ....')
        self.filename = filename

    def __del__(self):	# 类结束后触发
        print('回收操作系统资源')


f = Open('settings.py')
print('---main---')

exec的使用方法

'''
    exec的使用
    参数1:字符串形式的命令
    参数2:全局作用域(字典形式),如果不指定默认使用globals()
    参数3:局部作用域(字典形式),如果不指定默认使用locals()

'''
g = {'x': 1, 'y': 2}
l = {}
exec('''
global x,m
x = 10
m = 100
z = 3

''', g, l)
print(g)
print(l)

元类

一切皆对象,对象可以怎么用?
    1、都可以被引用,x=obj
    2、都可以当作函数的参数传入
    3、都可以当作函数的返回值
    4、都可以当作容器类型的元素, l=[func,time,obj,1]
    

定义一个类的方式

方式1:class
定义类的两种方式
方式一:class
class Chinese:
    country = 'China'

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

    def talk(self):
        print('%s is taking' % self.name)

Zhangsan =  Chinese('张三',18)
Zhangsan.talk()
方式2:type
# 定义类的三要素 (1、类名, 2、类的基类,3、类的名称空间)
class_name = 'Chinese'  # 类名
class_bases = (object,)  # 类的基类

# 类的属性
class_boby = ''' 
country = 'China'

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

def talk(self):
    print('%s is taking' % self.name)

'''

class_dic = {}  # 类的名称空间
exec(class_boby, globals(), class_dic)
# print(class_dic)
Chinese = type(class_name,class_bases,class_dic)
Zhangsan = Chinese('张三',18)   # 实例化
Zhangsan.talk()
使用元类来控制类的行为
'''
    定制元类来控制类的行为
'''


# raise  直接抛出异常


class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名的首字母必须大写')

        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,而且注释不能为空')

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)


class Chinese(object, metaclass=Mymeta):    # metaclass指定元类 默认为type
    '''
        aaa
    '''
    country = 'China'

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

    def talk(self):
        print('%s is taking' % self.name)

call 方法控制类的实例化行为

#    __call__方法
class Foo:
    def __call__(

class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)


obj = Foo()
obj(1, 2, 3, 4, a=1, b=2)

# 元类内部也应该有一个__call__方法,会调用Foo时触发执行,整个方法可以控制类的实例化行为

自定义元类控制类的实例化行为
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名的首字母必须大写')

        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,而且注释不能为空')

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

    def __call__(self, *args, **kwargs):    #  相当于:obj=Chinese('张三', 18)
        # 第一件事:先造一个空对象
        obj = object.__new__(self)
        # 第二件事:产生对象
        self.__init__(obj, *args, **kwargs)
        # 第三件事:返回对象
        return obj


class Chinese(object, metaclass=Mymeta):  # metaclass指定元类 默认为type
    '''
        aaa
    '''
    country = 'China'

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

    def talk(self):
        print('%s is taking' % self.name)


obj = Chinese('张三', 18)
print(obj.__dict__)

单例模式

# 单例模式
'''
	当应该类反复被调用时,为了可以节省内存,就是可以使用单例模式来运行

'''
class MySQL:
    __instance = None
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306

        
    # 这就是一个单例
    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj = cls()
            cls.__instance = obj
        return cls.__instance
    def conn(self):
        pass

    def execute(self):
        pass
    

obj1 = MySQL.singleton()
obj2 = MySQL.singleton()
print(obj1 is obj2)	# 他们指向的内存id都是一个

方式二使用元类来控制

class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

        self.__instance = None

    def __call__(self,*args,**kwargs):  # 相当于:obj=Chinese('张三', 18)
        if not self.__instance:
            obj = object.__new__(self)
            self.__init__(obj)
            self.__instance = obj
        return self.__instance


class MySQL(object,metaclass=Mymeta):
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306

    def conn(self):
        pass

    def execute(self):
        pass

obj1 = MySQL()
obj2 = MySQL()
print(obj1 is obj2)