Python--面向对象进阶

2018-11-28 08:52:41来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

isinstance和issubclass

isinstance

isinstance(obj1,obj2):判断对象与类之间的关系,判断第一个参数是否是第二个参数的实例。

>>> n1 = 10
>>> isinstance(n1, int) #判断n1是否是数字类型,如果是返回True如果不是防护False
True

>>> class A(object):
...     pass
...
>>> a1 = A()
>>> isinstance(a1, A)  # 判断a1是否是类A的对象,如果是返回True,如果不是返回False
True

type()函数和isinstance()函数两者有什么区别呢?

>>> print(type(1) is int)
True
>>> print(isinstance(1, int))
True

#从上面的结果看,两者的结果都是True,那么type()与isinstance()的区别在哪呢?
#从接下来的例子,就能够清晰看出来。

class A:pass
class B(A):pass

b = B()

print(isinstance(b, A)) # True
print(isinstance(b, B)) # True

print(type(b))  # <class '__main__.B'>
print(type(b) is B) # True
print(type(b) is A) # False

#总结:
    isinstance()是可以用在继承的关系上;而type()不能用来检测继承关系。

issubclass

issubclass(obj1,obj2):用来描述一个类与另一个类之间的关系,判断一个类是否是另一个类的子类

class A:pass
class B(A):pass

print(issubclass(B, A)) # True
print(issubclass(A, B)) # False

# 总结:
    类B是类A的子类,类A不是类B的子类

反射

什么是反射?

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

Python中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

四个可以实现自省的函数:hasattr getattr setattr delattr

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

def hasattr(*args, **kwargs): # real signature unknown
    """
    Return whether the object has an attribute with the given name.
    
    This is done by calling getattr(obj, name) and catching AttributeError.
    """
    pass
hasattr
def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value
    
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass
getattr
def setattr(x, y, v): # real signature unknown; restored from __doc__
    """
    Sets the named attribute on the given object to the specified value.
    
    setattr(x, 'y', v) is equivalent to ``x.y = v''
    """
    pass
setattr
def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.
    
    delattr(x, 'y') is equivalent to ``del x.y''
    """
    pass
delattr

类使用反射

类:静态属性 类方法 静态方法

class Student(object):
    name = "小白"

    @classmethod
    def check_course(cls):
        print("查看课程")

    @staticmethod
    def login():
        print("登录")

# 检查是否含有某属性
print(hasattr(Student, "login"))    # 检测Student类中是否有login方法,如果有返回True,没有返回False

# 通过反射查看静态属性
print(getattr(Student, 'name'))

# 通过反射调用方法
print(getattr(Student, "check_course"))  # 得到的是类方法的内存地址
getattr(Student, "check_course")()  # 调用类方法
print(getattr(Student, "login"))    # 得到的是静态方法的内存地址
getattr(Student, "login")() # 调用静态方法

# 一般情况hasattr和getattr联用, 示例:
num = input('>>>')  # 等待用户输入
if hasattr(Student, num):   # 判断用户输入的方法是否有,如果有才执行。
    getattr(Student, num)()

对象使用反射

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

    def func(self):
        print(" In func ")


a = A("小白")
print(a.name)
print(getattr(a, "name"))
getattr(a, "func")()

模块使用反射

import os
os.rename('__init__.py', 'init')
getattr(os, 'rename')('init', '__init__.py')    # os.rename('init', '__init__.py')

自定义模块使用反射

import sys
def wahaha():
    print("wahaha")

def qqxing():
    print("qqxing")

wahaha()
qqxing()

my_file = sys.modules['__main__']
getattr(my_file, 'wahaha')()
getattr(my_file, 'qqxing')()

反射总结

# 反射
# hasattr, getattr
# 类名.名字
    # getattr(类名, '名字')
# 对象名.名字
    # getattr(对象, '名字')
# 模块名.名字
    # import 模块
    # getattr(模块, '名字')
# 自己文件.名字
    # import sys
    # getattr(sys.modules['__main__'], '名字')

反射的应用

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "yanjieli"
# Date: 2018/11/25/025

import sys


class Int(object):
    def __init__(self, name):
        self.name = name


class Manager(Int):
    OPERATE_LIST = [
        ('创建课程', 'create_course'),
        ('招聘老师', 'create_Teacher'),
        ('查看课程', 'check_course'),
        ('查看学生信息', 'check_userinfo'),
    ]

    def create_course(self):
        print("创建课程")

    def create_Teacher(self):
        print("招聘老师")

    def check_course(self):
        print("查看课程")

    def check_userinfo(self):
        print("查看学生信息")


class Teacher(Int):
    OPERATE_LIST = [
        ('查看课程', 'check_course'),
        ('查看学生成绩', 'check_achievement'),
    ]

    def check_course(self):
        print("查看课程")

    def check_achievement(self):
        print("查看学生成绩")


class Student(Int):
    OPERATE_LIST = [
        ('查看课程', 'check_course'),
        ('选择课程', 'choose_course'),
        ('查看已选课程', 'choosed_course'),
        ('jsdklfjskld', 'aa'),
    ]

    def check_course(self):
        print("查看课程")

    def choose_course(self):
        print("选择课程")

    def choosed_course(self):
        print("查看已选课程")


def login():
    username = input('user:>>>')
    password = input('password:>>>')
    with open("user_info", encoding="utf-8") as f:
        for line in f:
            user, pwd, ident = line.strip().split("|")
            if username == user and password == pwd:
                print("欢迎您!%s" % user)
                return user, ident
        else:
            print("登录失败")
            exit()


def main():
    user, id = login()
    cls = getattr(sys.modules['__main__'], id)  # 通过反射拿到当前登录用户所属的类,如果是学生,则拿到学生类;
    operate_list = cls.OPERATE_LIST
    obj = cls(user)
    for index, i in enumerate(operate_list, 1):
        print(index, i[0])      # 打印出所有的功能
    option = int(input("option: >>>"))
    option_itme = operate_list[option -1]   # 拿到的是一个元组,比如:('创建课程', 'create_course'),
    getattr(obj, option_itme[1])()     # 通过反射拿到所输入的选项对应的方法并执行


main()
反射应用示例

内置方法

内置方法:内置方法就是不需要程序员定义,本身就存在类中的方法。内置方法又称为双下方法,魔术方法。

内置方法通常长成这样:__名字__。 每一个双下方法都有它自己的特殊意义

所有的双下方法,都不需要我们直接调用,都有另外一种自动触发它的方法。而是总有一些其他的 内置方法 特殊的语法 来自动触发这些 双下方法

__call__

__call__ 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。

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

    def __call__(self, *args, **kwargs):
        print('调用对象的__call__方法')


a = Person('张三', 24)  # 类Person可调用
a()  # 对象a可以调用
class A(object):
    def __call__(self, *args, **kwargs):
        print("执行了call方法")


class B(object):
    def __init__(self, cls):
        print("在实例化A之前做一些事情")
        self.obj = cls()
        self.obj()
        print("在实例化A之后做一些事情")


a = A()
a()     # 对象() == 相当于调用__call__方法

A()()   # 类名()() 相当于先实例化得到了一个对象,再对对象(), ==> 和上面的结果一样,相当于调用__call__方法
B(A)

__len__

计算对象的长度,如果类中定义了__len__方法,那么在对象中就能用内置函数len(obj),就会自动调用__len__方法。

class Foo(object):
    def __init__(self, s):
        self.s = s

    def __len__(self):
        print('调用了__len__方法, 计算长度')
        return len(self.s)


a = Foo("aaaa")
print(len(a))   # 自动调用了__len__方法
'''
打印结果:
调用了__len__方法, 计算长度
4
'''

__new__

网上经常有一个笑话“程序员可以自己new一个对象”, 到底new有什么作用呢?

__new__又称为构造方法,通过__init__()方法,我们知道初始化一个实例需要经历的几个步骤中的第一步就是在内存空间中开辟一个对象空间,这个对象空间是__init__方法开辟的么?其实不是,在init之前,实例化对象的第一步是通过__new__方法创建一个对象空间。

class Fuu():
    def __new__(cls, *args, **kwargs):  # 构造方法,构造了对象的空间
        print("执行了__new__方法")
        return object.__new__(cls)  # 或者super().__new(cls) # 调用了object类中的__new__方法

    def __init__(self, name, age):  # 初始化方法
        print("执行了init方法")
        self.name = name
        self.age = age


c1 = Fuu("小白", 18)
"""
就这样执行可以打印出:
执行了__new__方法
执行了init方法
"""

从上面的例子,我们可以更加清晰的得到初始化一个对象所经历的过程:

  1、实例化Fuu类,应该先执行其__new__方法。

  2、但是Fuu类中没有定义__new__方法,所以到object父类中执行object的__new__方法。

    回顾调用父类的两种方法:

      object.__new__(cls)

      super().__new__(cls)

  3、此时在内存中开辟一块对象空间。

  4、才执行init初始化方法,把地址传给self,给对象封装属性。

总结:构造方法__new__和初始化方法__init__之间的区别?

形象的说,类好比一个人类型的模板,__new__构造方法就是捏小人的过程(捏出来的每一个小人都是一样的),__init__初始化方法好比给小人穿衣服的过程(为对象封装属性),一定是先有__new__方法,后有__init__。

单例模式:一个类只有一个实例的时候,这种就叫做单例模式。

# 单例模式:一个类只有一个实例。
# 思考:如果使得一个类只能实例化一次,也就是这要控制只能开辟一次空间,就能实现单例模式。


class Singleinstance():
    __INSTANCE = None   # 通过设置一个标志位,用来使得只能运行一次new方法

    def __new__(cls, *args, **kwargs):
        if not cls.__INSTANCE:
            cls.__INSTANCE = object.__new__(cls)
        return cls.__INSTANCE

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

obj1 = Singleinstance('小白', 18)
obj2 = Singleinstance('小黑', 20)

print(obj1.name, obj1.age)
print(obj2.name, obj2.age)
print(obj1)
print(obj2)

>>>
小黑 20
小黑 20
<__main__.Singleinstance object at 0x000001C1657F6048>
<__main__.Singleinstance object at 0x000001C1657F6048>

'''
总结:由上打印可以看到不论实例化多少对象,都是一个对象,都会已最后一个为准。
'''

__str__ 与 __repr__

__str__

如果想把一个类的实例变成str类型,打印对象名的时候就执行__str__方法。

__str__ : str(obj),要求必须实现了__str__,要求这个方法的返回值必须是字符串str类型

三种场景会触发__str__方法:

  • (1)当你打印一个对象名的时候,就会就会触发__str__。  
  • (2)当你使用%s格式化输出对象的时候,也会触发__str__。
  • (3)强制转换数据类型的时候,也会触发__str__。
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __str__(self):
        return "%s %s %s" % (self.name, self.age, self.sex)

st1 = Student("小白", 18, "")
print(st1)                  #1.打印对象名,自动触发__str__方法
>>>
小白 18 男


student_list = []
student_list.append(st1)
for index, i in enumerate(student_list):
    print('%s %s'% (index, i))          #2.当使用%s格式化的时候,自动触发__str__

>>>
0 小白 18 男

__repr__

1、__repr__是__str__方法的备胎,如果有__str__就使用__str__,否则执行__repr__。

# (1)同时存在__str__和__repr__两个方法:
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __str__(self):
        return "str: %s %s %s" % (self.name, self.age, self.sex)

    def __repr__(self):
        return "repr: %s %s %s" % (self.name, self.age, self.sex)


st1 = Student("小白", 18, "")
print(st1)
>>>
str: 小白 18# (2)只存在__repr__方法时,再次打印对象名:
class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    # 注释掉__str__双下方法
    # def __str__(self):
    #     return "str: %s %s %s" % (self.name, self.age, self.sex)

    def __repr__(self):
        return "repr: %s %s %s" % (self.name, self.age, self.sex)


st1 = Student("小白", 18, "")
print(st1)         # 当__str__没有时,执行__repr__方法
>>>
repr: 小白 18 男

如果__repr__仅仅只是__str__的备胎,那么它就没有存在的意义了。所有__repr__还是有它自己的用武之地的时候:

2、如果使用内置函数repr(obj),或者通过%r格式化的时候,就会自动触发__repr__方法,不管有没有__str__,都调用__repr__。

st1 = Student("小白", 18, "")
print(repr(st1))
>>>
repr: 小白 18 男

小技巧:

python中之所有既有__str__,又有__repr__,__str__用于显示给用户看,__repr__用于显示给开发人员看。 下面就有一个偷懒的小办法:

__str__=__repr__

扩展知识:

class Fcc:
    def __str__(self):
        return "Fcc.str"

    def __repr__(self):
        return "Fcc.repr"


class Ecc(Fcc):
    def __str__(self):
        return "Ecc.str"

    def __repr__(self):
        return "Ecc.repr"


E1 = Ecc()
print(E1)
>>>
Ecc.str
'''
(1) 先去子类中查找,先调用__str__方法。
(2) 如果把子类的__str__方法注释掉,会去父类中查找父类的__str__方法
(3) 如果把父类的__Str__的方法注释掉,会再回到子类中执行备胎__repr__方法。
'''

__del__

__del__ 析构方法:在删除某个类的对象的时候,就会触发这个双下方法,再删除这个对象。即“del obj”执行这个删除对象的代码的时候,触发__del__析构方法。

运用的场景:文件资源,网络资源等。

class Obj(object):
    def __init__(self):
        self.file = open('file', 'w', encoding='utf-8')     # 打开一个文件

    def write_file(self, content):
        self.file.write(content)

    def __del__(self):  # 析构方法,是去归还/释放一些在创建对象的时候借用的一些资源
        self.file.close()   # 关闭文件
        print("执行了__del__方法")


obj = Obj()
obj.write_file("welcom")
del obj     # 当一执行del obj  就会自动触发__del__中的方法, 关闭文件然后打印出"执行了__del__方法"

__item__系列

__getitem__\__setitem__\__delitem__

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='小白'
print(f1.__dict__)

__hash__

哈希是一种算法,把一个对象转换为一串数字。

  • 对于相同的值在同一次程序的运行中是不会发生变化的。
  • 对于不同的值在同一次程序的运行中总数不同的。

扩展小知识:

一. 字典类型寻址快的原因:

  Q1:字典在内存中是如何存储的?

  Q2:为什么字典的key必须是可哈希的类型?

  Answer:字典通过对key转换得到的hash值直接找到内存中值的地址,一次过程key的hash值一直是一样的,所以快速。

二. set集合的去重机制:

  (1)必须通过哈希,因为在一次过程中,相同的值得到的哈希是一样的,如果有重复就不再添加。

  (2)但是hash算法也不是完全的靠谱,为了避免极微小机率出现两个值得hash值一样,就会比较两个值是否相等,再进行二次寻址。

  Answer:hash的结果找到一块内存地址,如果这个地址上没有数据,就说明之前没有重复的数据;如果这块地址上有一个数据存在了,才判断这个值和我要存储的值是否一样,如果一样则覆盖去重;如果不一样,二次寻址给这个值换个地方存

__eq__

__eq__双下方法,用来自定义判断两个对象是否相等,默认object类中中的eq方法是以内存地址作为判断标准,在运行"obj1 == obj2"时,就会自动触发__eq__双下方法。

# 默认object类中的__eq__方法以内存地址作为判断标准
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age


obj1 = A("小白", 18)
obj2 = A("小白", 18)
print(obj1 == obj2)      # obj1 == obj2 自动调用__eq__方法
# False

# 自定义__eq__方法
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):    #__eq__方法接受两个参数,一个是self,一个是其他对象
        if self.name == other.name and self.age == other.age:
            return True

obj1 = A("小白", 18)
obj2 = A("小白", 18)
print(obj1 == obj2)  # 相当于执行了obj1.__eq__(obj2)
# True

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Python连接三大数据库MS Sql Server、Oracle、MySQL

下一篇:python while循环