面向对象高级

目录

面向对象基础

一、定义类名的规范

驼峰命名法

二、查看名称空间

def func():
    pass

class dog():
    pass

print(func.__dict__)   # {}
print(dog.__dict__)   # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'dog' objects>, '__weakref__': <attribute '__weakref__' of 'dog' objects>, '__doc__': None}

三、对象的绑定方法

由对象来调用类内部的函数,称之为对象的绑定方法。
    对象的绑定方法特殊之处: 会将对象当做第一个参数传给该方法。

四、对象面子查找顺序

自己——类——报错

对象名字的查找顺序:  *******
    1.对象.属性,会先找对象自己的。
    2.若对象没有,会去找类的。
    3.若类没有,则会报错。
'''

五、新式类与经典类

- 新式类:广度优先
    1.凡是继承object的类或子孙类都是新式类。
    2.在python3中所有的类都默认继承object,都是新式类。

- 经典类:深度优先
    1.在python2中才会有经典类与新式类之分。
    2.在python2中,凡是没有继承object的类,都是经典类。

面向对象三大特性

一.继承

1.什么是继承?

   继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类(super),新建的类称为派生类或子类

python中继承分为:单继承和多继承

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

查看继承

>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如str)的实现。

2.继承与抽象

抽象: 抽象即抽取类似或者说比较像的部分

抽象分成两个层次: 

		1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

		2.将人,猪,狗这三个类比较像的部分抽取成父类。

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

**继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。**

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

3.继承与代码的重用性

==========================第一部分
例如

  猫可以:吃、喝、爬树

  狗可以:吃、喝、看家

如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:


#猫和狗有大量相同的内容
class 猫:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 爬树(self):
        # do something



class 狗:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 看家(self):
        #do something


==========================第二部分
上述代码不难看出,吃、喝是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:

  动物:吃、喝

     猫:爬树(猫继承动物的功能)

     狗:看家(狗继承动物的功能)

伪代码如下:
class 动物:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):

    def 爬树(self):
        print '喵喵叫'

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):

    def 看家(self):
        print '汪汪叫'


==========================第三部分
#继承的代码实现
class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = '猫'

    def climb(self):
        print('爬树')

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed='狗'

    def look_after_house(self):
        print('汪汪叫')


# ######### 执行 #########

c1 = Cat('小白家的小黑猫')
c1.eat()

c2 = Cat('小黑的小白猫')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()

4.派生

当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。叫做重写、覆盖
class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

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

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def bite(self, people):
        '''
        派生:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def attack(self, dog):
        '''
        派生:人有攻击的技能
        :param dog: 
        '''
        dog.life_value -= self.aggressivity

egg = Person('egon',10,1000)
ha2 = Dog('二愣子',50,1000)
print(ha2.life_value)
print(egg.attack(ha2))
print(ha2.life_value)

1.super():调用父类的属性

class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()
        #A.hahaha(self)
        print('B')

a = A()
b = B()
b.hahaha()
super(B,b).hahaha()

5.继承的顺序

在python2经典类中继承的顺序是: 深度优先

在python3新式类中继承的顺序是: 广度优先

6.issubcalss 判断是否为谁的子类

issubclass(a,B):判断a是不是B的子类
参数a:子类
参数B:父类

issubclass 判断传入的obj是不是Animal的子类

def manage(obj):

    if issubclass(type(obj), Animal):
        obj.eat()
    else:
        print('不是动物')

7.isinstance判断数据结构的类,是否为谁的实例化

# 判断是否为谁的实例化
isinstance(a,int) 判断类型

参数a :要判断的对象

参数int:判断的类型

isinstance案例:
def add_num(a, b):
    # if type(a) == int and type(b) == int:   # 等于下面一句
    if isinstance(a,int) and isinstance(b,int):
        return a+b
    else:
        print("不能相加")

print(add_num('你', 12))
#结果:不能相加

8.子类继承父类,并添加或重写的两种方法

1.父类.__init__方法

class Animal():
    def __init__(self, name, eat, run):
        self.name = name
        self.eat = eat
        self.run = run
        print(f'{self.name}会{self.eat}')
        print(f'{self.name}会{self.run}')

class Sunwukong(Animal):
    def __init__(self, name, eat, run, aa):
        Animal.__init__(self,name, eat, run)
        self.aa = aa

    def skill(self):
        print(self.name,'有72变化,筋斗云,如意金箍棒')

sun = Sunwukong('齐天大圣','吃','跑','sb')

sun.skill()

# 结果:
齐天大圣会吃
齐天大圣会跑
齐天大圣 有72变化,筋斗云,如意金箍棒

2.super().__init方法

指向父类的名称空间

class Animal():
    def __init__(self, name, eat, run):
        self.name = name
        self.eat = eat
        self.run = run
        print(f'{self.name}会{self.eat}')
        print(f'{self.name}会{self.run}')

class Sunwukong(Animal):
    def __init__(self, name, eat, run, aa):
        super().__init__(name, eat, run)
        self.aa = aa

    def skill(self):
        print(self.aa,'有72变化,筋斗云,如意金箍棒')

sun = Sunwukong('齐天大圣','吃','跑','sb')
sun.skill()

# 结果 :
齐天大圣会吃
齐天大圣会跑
sb 有72变化,筋斗云,如意金箍棒

9.检查super的继承顺序mro()方法

在python3中提供了一个查找新式类查找顺序的内置方法.

​ mro(): 会把当前类的继承关系列出来

​ 多继承的情况下: 从左到右

class A:
    def test(self):
        print('from A.test')
        super().test()
class B:
    def test(self):
        print('from B.test')
class C(A, B):
    pass

c = C()
# 检查super的继承顺序
print(C.mro())

# 结果:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

10.钻石继承(棱形继承)

1.python的类可以继承多个类,JAVA,C#只能继承一个类

2.python的类如果继承了多个类,那么寻找方式有两种,分别是:深度优先和广度优先

3.继承顺序

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

二、多态

多态的三种表现形式

继承父类  ----   莲藕度高,扩展性低  	--- 让子类遵循父类的规范
继承抽象类 ----  莲藕度极高,扩展性极低   --- 让子类强制遵循父类发一套规范
鸭子类型 ----   莲藕度低,扩展性高  		---让所有的类都遵循一套定义方法规范
多态:一种事务具备多种不同下形态

官方解释:多个不同对象可以响应同一个方法,产生不同的结果

比如:动物有多种形态:人,狗,猪
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.抽象类

1.什么是抽象类?
    在python内置的abc模块中,有一个抽象类。

2.抽象类的作用:
    让子类必须遵循父类的编写规范。

3.如何实现抽象类
    - 父类需要继承abc模块中,metaclass=abc.ABCMeta
    - 在父类的方法中,需要装饰上 abc.abstractmethod
 
 注意: 在python中不推荐使用抽象类。
 注意: 子类必须按照父类的方法编写规范,缺一不可。(只要父类中有几个抽象方法,子类就必须要定义几个)

调用同一种方法,响应不同的结果:

class A(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def eat(self):
        pass

class Dog(A):
    def eat(self):
        print('狗狗吃饭饭。。。')

class Pig(A):
    def eat(self):
        print('猪猪看电视。。。')

dog = Dog()
pig = Pig()

dog.eat()
pig.eat()
##
狗狗吃饭饭。。。
猪猪看电视。。。

2.鸭子类型

1.什么是鸭子类型?
    不同的对象,只要长得像鸭子,动作行为像鸭子,那它就是鸭子!

    鸭子类型是多态的一种表现形式。

2.为什么要有鸭子类型?
    不同对象,先抽象出相同类型的方法,给他们定制一套统一的规范。
    所有的类,在定义时都按照统一的规范进行编写。

    - 多态的三种表现形式:
        - 继承父类    ****
            - 耦合度高,程序的可扩展性低

        - 继承抽象类  ***
            - 耦合度极高,程序的可扩展性极低

        - 鸭子类型:   *****

调用的方法相同

# 猪类
class Pig:
    def eat(self):
        print('bia唧...')

    def speak(self):
        print('哼哼哼...')

# 猫类
class Cat:
    def eat(self):
        print('咬ji 咬ji....')

    def speak(self):
        print('喵喵喵...')

# 狗类
class Dog:
    def eat(self):
        print('舔 ji 舔ji...')

    def speak(self):
        print('汪汪汪...')

def SPEAK(animal):
    animal.speak()

# 产生对象
dog = Dog()
pig = Pig()
cat = Cat()

# 多态之炫技
SPEAK(dog)
SPEAK(pig)
SPEAK(cat)

自定义统计长度

str1 = 'tank is very handsome!!!'
list1 = ['tank', 'is', 'very', 'handsome!!!']

print(str1.__len__())
print(list1.__len__())


# 自定义统计长度函数
def LEN(obj):
    return obj.__len__()

print(LEN(str1))
print(LEN(list1))

print(len(str1))
print(len(list1))

三、封装(最重要)

封装:

1.面向对象最重要的是封装

2.隐藏对象的属性和实现细节,仅对外提供公共访问方式

1.封装原则

1.将不需要对外提供的内容都隐藏起来

2.把属性都隐藏,提供公共方法对其访问。

2.封装好处

1.讲变化隔离

2.便于使用

3.提高复用性

4.提高安全性

3.私有属性和私有方法

私有属性、私有方法:1.让一些关键的数据,变成私有更加的安全
                    2.不是随意可以更改的
                    3.在属性,和方法前面加’__‘,变成私有,那么外界就不可以直接调用修改。
                    4.但是:在类的内部可以定义一个函数,方法调用修改。使用者直接调用这个函数就可以了。这个函数就是接口
                    5.可以在这个函数、方法加条件限制,而不是任意的改动



例子:定义成私有属性,在函数内部可以修改,那么用户调用这个函数也就可以直接修改私有属性,但是可以给这个函数加‘条件’,比如下面:小于300可以修改,否则不能修改
class student:

    def __init__(self, name, max):
        self.name = name
        self.__max = max
        # print(self.__max)

    def max(self, new_max):
        if new_max < 300:   # 增加修改的条件
            print('修改成功')
            self.__max = new_max
            print(self.__max)
        else:
            print('修改失败')

jeff =student('jeff',100)

jeff.max(200)
# jeff.max(500)

1.私有属性

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

原理: 替换变量名称 方法名 替换为:_类名__方法名

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

2.私有方法

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况
class A:
    def fa(self):
        print('from A')
    def test(self):
        self.fa()

class B(A):
    def fa(self):
        print('from B')

b=B()
b.test()
from B
 

#把fa定义成私有的,即__fa
class A:
    def __fa(self): #在定义时就变形为_A__fa
        print('from A')
    def test(self):
        self.__fa() #只会与自己所在的类为准,即调用_A__fa

class B(A):
    def __fa(self):
        print('from B')

b=B()
b.test()
from A

3.访问限制机制

	把内部方法、属性私有化,让外面不能直接调用,二十通过内部给的一个接口调用。这个接口可以控制调用方法的顺序,条件等
class ATM:
    # 取钱功能:
    # 1.插入磁卡
    def __insert_card(self):
        print('开始插卡...')
        pass

    # 2.输入密码
    def __input_pwd(self):
        print('输入密码...')
        pass

    # 3.输入取款金额
    def __input_bal(self):
        print('输入取款金额...')
        pass

    # 4.吐钱
    def __output_money(self):
        print('开始吐钱...')
        pass

    # 5.打印流水账单
    def __print_flow(self):
        print('打印流水账单...')
        pass

    # 取款顺序规范接口:控制内部方法调用的顺序
    def withdraw(self):
        # 1.插入磁卡
        self.__insert_card()

        # 2.输入密码
        self.__input_pwd()

        # 3.输入取款金额
        self.__input_bal()

        # 4.吐钱
        self.__output_money()

        # 5.打印流水账单
        self.__print_flow()

# 生成一个对象
amt_obj = ATM()
amt_obj.withdraw()  # 内部给的一个接口

# 结果:
开始插卡...
输入密码...
输入取款金额...
开始吐钱...
打印流水账单...

4.封装与扩展性

#类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
        return self.__width * self.__length


#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area


#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high


#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()

5.组合

'''
夺命三问:
    1.什么是组合?
        组合指的是一个对象中,包含另一个或多个对象。

    2.为什么要用组合?
        减少代码的冗余。

    3.如何使用组合?

耦合度:
    耦: 莲藕 ---> 藕断丝连
    - 耦合度越高: 程序的可扩展性越低。
    - 耦合度越低: 程序的可扩展性越高。

总结:
    - 继承:
        继承是类与类的关系,子类继承父类的属性/方法,子类与父类是一种 “从属” 关系。

    - 组合:
        组合是对象与对象的关系,一个对象拥有另一个对象中的属性/方法,是一种什么有什么的关系。

ps:添加额外的年月日

1.普通方法

代码莲藕,不容易修改和后期的维护

class People:
    def __init__(self, name, age, sex, year, month, day):
        self.name = name
        self.age = age
        self.sex = sex
        self.year = year
        self.month = month
        self.day = day

    def tell_birth(self):
        print(f'''
        ===== 出生年月日 =====
            年: {self.year}
            月: {self.month}
            日: {self.day}
        ''')
# 老师类
class Teacher(People):
    def __init__(self, name, age, sex, year, month, day):
        super().__init__(name, age, sex, year, month, day)
# 学生类
class Student(People):
    def __init__(self, name, age, sex, year, month, day):
        super().__init__(name, age, sex, year, month, day)


tea1 = Teacher('tank', 17, 'male', 2002, 6, 6)
stu1 = Student('HCY', 109, 'female', 1910, 11, 11)

print(tea1.name, tea1.age, tea1.sex)
tea1.tell_birth()
print(stu1.name, stu1.age, stu1.sex)
stu1.tell_birth()


# 结果:
jeff 23 male

        ===== 出生年月日 =====
            年: 1996
            月: 6
            日: 5
        
陈嘉旻 20 female

        ===== 出生年月日 =====
            年: 1999
            月: 6
            日: 4

2.组合方法

扩展性高

关键之处:

​ 1.添加一个完全独立的功能,生成对象

​ 2.将这个独立功能的对象与其他对象绑定关系(组合)

class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
# 老师类
class Teacher(People):
    def __init__(self, name, age, sex):
        super().__init__(name, age, sex)
# 学生类
class Student(People):
    def __init__(self, name, age, sex):
        super().__init__(name, age, sex)
# 日期类
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def tell_birth(self):
        print(f'''
        ===== 出生年月日 =====
            年: {self.year}
            月: {self.month}
            日: {self.day}
        ''')
# 生成两个对象
tea1 = Teacher('jeff', 23, 'male')
date_obj = Date(1996, 6, 5)

# 两个对象绑定关系(组合)
tea1.date_obj = date_obj  

print(tea1.name, tea1.age, tea1.sex)
tea1.date_obj.tell_birth()

# 结果:
jeff 23 male

        ===== 出生年月日 =====
            年: 1996
            月: 6
            日: 5

四、英雄乱斗案列

import random
import time

class Hero:
    def __init__(self, name, level, blood, att, q_hurt, w_hurt, e_hurt):
        # self.name = name
        # self.level = level
        # self.blood = blood
        # self.att = att
        # self.q_hurt = q_hurt
        # self.w_hurt = w_hurt
        # self.e_hurt = e_hurt

        # 大神写法
        lcs = locals()
        lcs.pop('self')
        self.__dict__.update(lcs) 


    def attack(self, enemy):
        enemy.blood -= self.att
        print('%s 对 %s 进行了普通攻击,造成 %s 的伤害 %s剩余: %sHP' % (self.name, enemy.name, self.att, enemy.name,enemy.blood))
        if enemy.blood <= 0:
            print('%s把%s使用普通攻击击杀了' % (self.name, enemy.name))

    def Q(self, enemy):
        enemy.blood -= self.q_hurt
        print('%s 对 %s 释放了Q技能,造成 %s 的伤害 %s剩余: %sHP'
              % (self.name, enemy.name, self.q_hurt, enemy.name, enemy.blood))
        if enemy.blood <= 0:
            print('%s把%s使用普通攻击击杀了' % (self.name, enemy.name))

    def W(self, enemy):
        enemy.blood -= self.w_hurt
        print('%s 对 %s 释放了W技能,造成 %s 的伤害 %s剩余: %sHP'
              % (self.name, enemy.name, self.w_hurt, enemy.name, enemy.blood))
        if enemy.blood <= 0:
            print('%s把%s使用普通攻击击杀了' % (self.name, enemy.name))

    def E(self, enemy):
        enemy.blood -= self.e_hurt
        print('%s 对 %s 释放了E技能,造成 %s 的伤害 %s剩余: %sHP'
              % (self.name, enemy.name, self.e_hurt, enemy.name, enemy.blood))
        if enemy.blood <= 0:
            print('%s把%s使用普通攻击击杀了' % (self.name, enemy.name))


yx1 = Hero(name='诸葛亮', level=15, blood=5000, att=100, q_hurt=1000, w_hurt=300, e_hurt=2000)
yx2 = Hero(name='后裔', level=15, blood=5000, att=2000, q_hurt=0, w_hurt=500, e_hurt=1000)
yx3 = Hero(name='王昭君', level=15, blood=5000, att=2000, q_hurt=0, w_hurt=500, e_hurt=1000)
yx4 = Hero(name='吕布', level=15, blood=5000, att=2000, q_hurt=0, w_hurt=500, e_hurt=1000)


while True:
    # 攻击方式随机
    funcs = {1: Hero.Q, 2: Hero.W, 3: Hero.E, 4: Hero.attack}
    func_index = random.randint(1, 4)
    func = funcs[func_index]

    # 英雄随机
    heros = {1: yx1, 2: yx2, 3: yx3, 4: yx4}
    heros_index = random.randint(1, len(heros))
    # 攻击英雄随机
    hero = heros[heros_index]

    # 剩余英雄,排除自己打自己(重新做个字典)
    othor_heros = {}

    # 遍历
    new_index = 1
    for k, v in heros.items():
        if v != heros[heros_index]:
            othor_heros[new_index] = v
            new_index += 1

    # 从剩余的英雄中随机挑选一个攻击
    enemy_index = random.randint(1, len(othor_heros))
    # 被攻击随机英雄
    enemy = othor_heros[enemy_index]

    # 执行
    func(hero, enemy)

    # 血量为0
    if enemy.blood <= 0:
        break
    # time.sleep(0.5)

五、绑定方法

classmethod:
    是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “类的绑定方法”。

staticmethod:
    翻译: 静态方法
    是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “非绑定方法”。

- 对象的绑定方法:
    - 由对象来调用,由谁来调用,会将谁(对象)当做第一个参数传入。

- 类的绑定方法:
    - 由类来调用,由谁来调用,会将谁(类)当做第一个参数传入。

- 非绑定方法:
    - 可以由对象或类来调用,谁来调用都是一个普通方法(普通函数),方法需要传入几个参数,就得传入几个。

1.对象绑定方法

没有被任何装饰器装饰的方法,为对象量身定制

对象.方法(),自动将对象单座第一个参数传入self

class OldboyStudent:
    school = 'oldboy'

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

    def choose_course(self):
        print(f'{self.name} choosing course')

    def func(self):
        print('from func')
      
stu1 = OldboyStudent('nick', 18, 'male')
stu2 = OldboyStudent('sean', 17, 'male')
stu3 = OldboyStudent('tank', 19, 'female')

print(stu1.name)   # nick
print(stu1.school)   # oldboy

2.类的绑定方法@classmethod

classmethod:
    是一个装饰器,给在类内部定义方法中装饰,将类内部的方法变为 “类的绑定方法”。

例子:

class Dog():
    def eat(self):
        print('狗狗吃饭饭。。。')

class Pig():
    @classmethod  # 将此方法对象绑定
    def eat(self):
        print('猪猪看电视。。。')

# 对象绑定方法,需要实例化出一个对象
keji = Dog()
keji.eat()

# 类绑定方法,不需要对象,直接通过类
zhu = Pig.eat()

例子2:进阶高级

class DB:
    __data = 'jeff is very handsome!!!'
    def __init__(self, user, pwd, role):
        self.user = user
        self.pwd = pwd
        self.role = role
        
    # 查看数据方法
    @classmethod  # 类绑定
    def check_db(cls, user, pwd, role):  # cls --》指的是类
        # 在类方法内部调用类产生一个实例 ---》 对象
        obj = cls(user, pwd, role)    # 再类的内部实例化出一个对象,供内部使用

        # 1.查看数据前,必须要通过校验
        if obj.user == 'tank' and obj.pwd == '123' and obj.role == 'admin':
            print('检验通过..')
            print(cls.__data)
            return cls.__data
        
# 类绑定方法了
DB.check_db('tank', '123', 'admin')
#  结果:
检验通过..
jeff is very handsome!!!

3.非绑定方法(静态方法)@staticmethod

非绑定方法:
    - 可以由对象或类来调用,谁来调用都是一个普通方法(普通函数),方法需要传入几个参数,就得传入几个。
1.不用非绑定方法,打印的是func函数的地址
class Foo:
    def func(res):
        print(res)
obj = Foo()
obj.func()
#  <__main__.Foo object at 0x000001FF9F5D82E8>

2.对象调用非绑定方法
class Foo:
    @staticmethod
    def func(res):
        print(res)
# 产生了obj对象
obj = Foo()
obj.func(123)  # 123

3.类直接调用非绑定方法
class Foo:
    @staticmethod
    def func(res):
        print(res)
Foo.func(1234)
#  1234

4、@property装饰器

装饰器:为了调用方式一致,方便使用者
@property首先创建一个age对象,所以@setter、@deleter要加函数名
        :这里的age是装饰方法的名称

@property(获取私有) :把一个方法伪装成普通属性,通常函数名和属性名保持一致(方便使用者调用)
@函数名.setter(修改私有):函数名和属性名保持一致
@函数名.deleter(控制删除):
class A:
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    @property   # 获取属性时,触发下面
    # 获取伪装成普通属性,通常函数名伪装成属性名
    def age(self):
        return self.__age

    @age.setter  # 修改属性时,触发下面
    def age(self, new_age):
        self.__age = new_age
        print('修改成功')

    @age.deleter  # 删除属性时,触发下面
    def age(self):
        del self.__age
        print('删除成功,已经没有了')
# 查看
jeff = A('jeff', 50)
print(jeff.age)  #  50

# 修改
jeff.age = '100'
print(jeff.age)  # 修改成功   100

# 删除
del jeff.age
print(jeff.age)  # 报错,删除成功,已经没有了

六.反射

反射使用场景:

​	1.反射就是对属性的增删改查,但是如果直接使用内置的 dict来操作,语法繁琐,不好理解
​        2.如果对象是别人提供的,判断这个对象是否满足要求

hasattr(p,'name'):查找p对象中是否存在name属性
getattr(p,'name'):取值,p对象中name属性
setattr(p,'name','jeff'):添加,为p对象中添加name属性jeff
delattr(p,'name'):删除,删除p对象中name属性

1、hasattr: 查找-反射

foo_obj对象中是否存在x属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y
foo_obj = Foo(10, 20)
# 通过字符串x 判断对象中是否有 x属性
print(hasattr(foo_obj, 'x'))  # True
print(hasattr(foo_obj, 'z'))  # False

2、getattr :取值 反射

getattr(foo_obj,'x'):取值p对象中name属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

foo_obj = Foo(10, 20)

res = getattr(foo_obj, 'x')
print(res)  # 10

3、setattr: 添加-反射

为foo_obj对象中添加z属性30

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

foo_obj = Foo(10, 20)


setattr(foo_obj, 'z', 30)  # 为对象添加z属性值为30
print(hasattr(foo_obj, 'z'))  # True

4、delattr: 删除-反射

删除foo_obj对象中x属性

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

foo_obj = Foo(10, 20)

delattr(foo_obj, 'x')  # 删除对象中x属性
print(hasattr(foo_obj, 'x'))  # False

5.反射应用

通过用户输入的字符串,判断是否存在,getattr取值,加括号调用相应的功能函数

class FileControl:

    def run(self):
        while True:
            # 让用户输入上传或下载功能的命令:
            user_input = input('请输入 上传(upload) 或 下载(download) 功能:').strip()

            # 通过用户输入的字符串判断方法是否存在,然后调用相应的方法
            if hasattr(self, user_input):
                func = getattr(self, user_input)
                func()
            else:
                print('输入有误!')

    def upload(self):
        print('文件正在上传...')

    def download(self):
        print('文件正在下载...')

file = FileControl()
file.run()

七、类的内置方法(魔法方法)

魔法归总

类的内置方法(魔法方法):
    凡是在类内部定义,以__开头__结尾的方法,都是类的内置方法,也称之为魔法方法。
    类的内置方法,会在某种条件满足下自动触发。

内置方法如下:
    __new__: 在__init__触发前,自动触发。  调用该类时,内部会通过__new__产生一个新的对象。
    __init__: 在调用类时自动触发。    通过产生的对象自动调用__init__()
    __getattr__: 在 “对象.属性” 获取属性时,若 “属性没有” 时触发。
    __getattribute__: 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发。
        # 注意: 只要__getattr__ 与 __getattribute__ 同时存在类的内部,只会触发__getattribute__。
   __setattr__:当 “对象.属性 = 属性值” , 添加或修改属性时触发
   __call__ : 在调用对象 “对象 + ()” 时触发。 即:对象() 或者 类()()
   __str__  : 在打印对象时触发。 # 注意: 该方法必须要有一个 “字符串” 返回值。
   __getitem__: 在对象通过 “对象[key]” 获取属性时触发。
   __setitem__: 在对象通过 “对象[key]=value值” 设置属性时触发。
   __gt__,__lt__,__eq__:自定义比较对象大小双下:gt、lt、eq
   __enter__:  进入文件时,开打文件时执行。返回值:self
   __exit__:  退出文件时,报错中断、或者代码执行完时执行。 返回值:可以有返回值,是bool类型
   __del__ : 手动删除时立马执行,或者程序运行结束时自动执行
    		使用场景:当你的对象使用过程中,打开了不属于解释器的资源;例如,文件,网络端口
   __slots__:原理,给对象声明只有某些属性,从而删除不必要的内存,不能添加新属性
    		使用场景:1.优化对象内存  2.限制属性数量
   __enter__:  文件打开前自动触发
   __exit__:   文件代码执行完自动触发
  # with开始时自动触发
    def __enter__(self):
        # print('hahaha')
        self.open()
        return self

    # with代码块允许结束之后自动触发
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
"""
题目:请在Context类中添加代码完成该类的实现
class Context:
	pass

with Context() as ctx:
	ctx.do_something()
"""
class Context:
  def __enter__(self,*args,**kwargs):
    return self
  
  def __exit__(self,*args,**kwargs):
    pass
  
  def do_something(self):
    pass
  
with Context() as ctx:
	ctx.do_something()

1.双下new

因为类继承了object,所有在调用类之前会自动先执行双下new来创建对象。这里的双下new重写了object中的双下new,所以此时不能创建出对象。
class Demo():
    def __new__(cls, *args, **kwargs):
        print('此处是__new__方法的执行')
        # python内部通过object调用内部的__new__实现产生一个空的对象  ---> 内存地址

    def __init__(self):
        print('此处是__init__方法的执行')
# 只是实例化对象
demo_obj = Demo()
# 结果:
此处是__init__方法的执行

2.双下getattr

双下getattr: 在 “对象.属性” 获取属性时,若 “属性没有” 时触发。
class Demo():
    # __getattr__: 在 “对象.属性” 获取属性时,若 “属性没有” 时触发。
    def __getattr__(self, item):
        print('此处是__getattr__方法的执行')
        print(item)

        # return 想要返回的值
        return 'tank is very very handsome!!!'

demo_obj = Demo()
res = demo_obj.x  # 获取属性,赋值
# 打印返回值
print(res)
# 结果:
此处是__getattr__方法的执行
x
jeff is very very handsome!!!


3.双下getattribute

 # 条件: __getattribute__: 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发。
    
    class Demo():
    # 条件: __getattribute__: 在 “对象.属性” 获取属性时,无论 "属性有没有" 都会触发。
    def __getattribute__(self, item):
        print(item, '<-----打印属性名字')
        # print(self.__dict__)
        # return self.__dict__[item]
        # 注意: 此处不能通过 对象.属性,否则会产生递归调用,程序崩溃
        # getattr: 内部调用了 ----> __getattribute__
        # return getattr(self, item)

demo_obj = Demo()
demo_obj.x

# 结果:
x <-----打印属性名字

4.双下setattr

# __setattr__:当 “对象.属性 = 属性值” , 添加或修改属性时触发

class Demo():
    y = 20
    def __setattr__(self, key, value):
        print('此处是__setattr__方法的执行')
        print(key, value)
        
		# 循环递归,报错
        # self.key = value
        
jeff = Demo()
jeff.y = 10
# 结果:
此处是__setattr__方法的执行
y 10

5.双下call

# 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 双下__call__ 方法的执行是由对象后加括号触发的.
# 即:对象() 或者 类()()

class Demo():
    def __call__(self, *args, **kwargs):
        print('此处是__call__方法的执行')

        # 调用对象时返回的值
        return [1, 2, 3, 4, 5]
    def y(self):
        pass

jeff = Demo()
jeff()
# 结果:
此处是__call__方法的执行

6.双下str

# __str__  : 在打印对象时触发。 # 注意: 该方法必须要有一个 “字符串” 返回值。
# print 时内部自动调用__str__
class Demo():
    def __str__(self):
        res = '此处是__str__方法的执行'
        # 必须有返回值
        return res

jeff = Demo()
print(jeff)
# 结果:
此处是__str__方法的执行

7.双下getitem

# 在对象通过 “对象[key]” 获取属性时触发。
class Demo():
    def __getitem__(self, item):
        print('此处是__getitem__方法的执行')
        print(item)

jeff = Demo()
jeff['y']
# 结果:
此处是__getitem__方法的执行
y

8.双下setitem

# 在对象通过 “对象[key]=value值” 设置属性时触发。

class Demo():
    def __setitem__(self, key, value):
        print('此处是__setitem__方法的执行')
        print(key, value)

        print(self.__dict__)
        self.key = value  # {'key': value}   {'key': 123}
        print(self.__dict__)  # 

jeff = Demo()
jeff['y'] = 123
# 结果:
此处是__setitem__方法的执行
y 123
{}
{'key': 123}

9.自定义比较对象大小双下:gt、lt、eq

可以利用这三个自定义比较对象:
__gt__:greater than  大于缩写
__lt__:less than  小于缩写
__eq__:equal   等于缩写
class Student(object):
    def __init__(self, name, height, age):
        self.name = name
        self.height = height
        self.age = age

    # 比较大于时触发
    def __gt__(self, other):
        return self.height > other.height

    # 比较小于时触发
    def __lt__(self, other):
        return self.age < other.age

    # 比较等于时触发
    def __eq__(self, other):
        # if self.age == other.age:  # 比较两个对象的年龄
        if self.age == other.age and self.height == other.height:  # 比较年龄和身高
            return '年龄身高一样'
        return '年龄身高不一样'


stu1 = Student("jeff", 170, 25)
stu2 = Student("make", 170, 25)

print(stu1 > stu2)
print(stu1 < stu2)
print(stu1 == stu2)

10.上下文管理双下enter进入文件时,双下__exit__离开

# __enter__:1.进入文件,开打文件时执行
            2.函数应该返回自己self
# __exit__:1.退出文件、报错中断、或者代码执行完时执行
        2.可以有返回值,是bool类型
        
class MyOpen(object):
    def __init__(self, path):
        self.path = path
    
    # 进入文件时触发
    def __enter__(self):
        self.file = open(self.path)
        print("文件打开....")
        return self
    
    # 离开文件时触发
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("文件关闭")
        # print(exc_type,exc_val,exc_tb)
        self.file.close()  # 关闭文件
        return True


with MyOpen('a.txt') as f:
    print(f.file.readline())

# f.file.readline()   # 文件已经关闭了,不能读

11.双下del删除时触发

__del__ : 
    执行时机:手动删除时立马执行,或者程序运行结束时自动执行
    使用场景:当你的对象使用过程中,打开了不属于解释器的资源;例如,文件,网络端口

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

    def __del__(self):
        print('删除时触发')

    # 删除时关闭
    def __del__(self):
        self.file.close()

jeff = A('jeff')
del jeff
# jeff.name  # 已经删除,无法访问了

12.双下__slots__优化对象内存

slots:原理,给对象声明只有某些属性,从而删除不必要的内存,不能添加新属性
    1.优化对象内存
    2.限制属性数量

__slots__案例:slots使用

# 第一种:不优化内存
import sys
class Person:
    slots = ['name']  # 声明名称空间只有name属性
    def __init__(self, name):
        self.name = name
p = Person('jeff')

print(sys.getsizeof(p))
# 结果:56


# 第二种:优化内存
import sys
class Person:
    __slots__ = ['name']  # 声明名称空间只有name属性

    def __init__(self, name):
        self.name = name
p = Person('jeff')
print(sys.getsizeof(p))
#结果:48          #####内存减少了8

八、单例

单例:一个对象,多次使用相同的。节约内存空间

单例模式:
    指的是在确定 "类中的属性与方法" 不变时,需要反复调用该类,
    产生不同的对象,会产生不同的内存地址,造成资源的浪费。

    让所有类在实例化时,指向同一个内存地址,称之为单例模式。  ----> 无论产生多个对象,都会指向 单个 实例。

    - 单例的优点:
        节省内存空间。

单列模式分类

单例模式: (面试让你手撸,一定要背下来。)
    1.通过classmethod  类绑定方法
    2.通过装饰器实现
    3.通过__new__实现
    4.通过导入模块时实现
    5.通过元类实现。

1.classmethod类绑定单例

# 第一种:不用单例
# 两个不同的对象,地址不一样
class MySQL():
    def __init__(self, host, port):
        self.host = host
        self.port = port

    def singleton(self):
        pass

obj1 = MySQL('192.168.0.101',3306)
obj2 = MySQL('192.168.0.101',3306)
print(obj1)
print(obj2)
# 结果:
<__main__.MySQL object at 0x000001FE458A8470>
<__main__.MySQL object at 0x000001FE458A8518>


# 第二种:用单例
# 一个对象重复使用,指向同一个地址,节约内存
class MySQL():
    __instance = None  # 标识对象是否已经存在
    def __init__(self, host, port):
        self.host = host
        self.port = port
        
    @classmethod
    def singleton(cls, host, port):  # 单例方法
        if not cls.__instance:
            obj = cls(host,port)
            cls.__instance = obj
        # 如果__instance有值,证明对象已经存在,则直接返回该对象
        return cls.__instance

obj1 = MySQL.singleton('192.168.0.101',3306)
obj2 = MySQL.singleton('192.168.0.101',3306)
print(obj1)
print(obj2)
# 结果:
<__main__.MySQL object at 0x000001D2B79D8518>
<__main__.MySQL object at 0x000001D2B79D8518>

2.双下new方法单例

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            res = super(Singleton, cls)
            cls._instance = res.__new__(cls, *args, **kwargs)
        return cls._instance

class A(Singleton):
    pass

obj1 = A()
obj2 = A()
print(obj1)
print(obj2)
# 结果:
<__main__.A object at 0x000001F81A3B8518>
<__main__.A object at 0x000001F81A3B8518>
class Singleton:
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            # 造一个空对象
            cls.__instance = object.__new__(cls)

        return cls.__instance

3.装饰器方法单例

# 方式三: 装饰器
def singleton(cls):  # cls---> Father

    _instance = {}
    def inner(*args, **kwargs):
        if cls not in _instance:
            obj = cls(*args, **kwargs)
            _instance[cls] = obj

        return _instance[cls]

    return inner

@singleton
class Father:
    pass
print(Father())
print(Father())

4.模块导入

# Singleton.py文件中:

class SingletonCls:
    pass

obj = SingletonCls()

# 另一个文件
from Singleton import obj
print(obj)
from Singleton import obj
print(obj)
from Singleton import obj
print(obj)

九.元类

注意:只要继承了type 那么这个类就变成了一个元类

__call__在调用对象时触发

1.什么是元类?

在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即元类可以简称为类的类

2.为什么要使用元类?

元类是负责产生类的,所以我们学习元类或者自定义元类的目的:是为了控制类的产生过程,还可以控制对象的产生过程

3.如何用元类

创建类的方法有两种:

大前提:如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程

该实例化的目的是为了得到一个类,调用的是元类

方式一:用的默认的元类type

方式二:自定义元类继承type

class Mate(type):

    def __call__(self, *args, **kwargs):
        if args:
            raise Exception('不允许')
        return super().__call__(*args,**kwargs)

# 定义了一个元类
class A(metaclass=Mate):
    def __init__(self, name):
        self.name = name

a = A(name='jeff')
print(a.name)
posted @ 2019-08-20 01:14  Jeff的技术栈  阅读(271)  评论(0编辑  收藏  举报
回顶部