Python - - 面向对象 - - 面向对象进阶

Posted 小Q渺晓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python - - 面向对象 - - 面向对象进阶相关的知识,希望对你有一定的参考价值。

目录

  • isinstance和issubclass
  • 反射
    • setattr
    • delattr
    • getattr
    • hasattr
  • str__和__repr
  • del
  • item系列
      - getitem
      - setitem
      - delitem
  • new
  • call
  • len
  • hash
  • eq

1 isinstance和issubclass

  • isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class A:pass
a = A()
print(isinstance(a, A))

# 结果呈现
True
  • issubclass(sub, super)检查sub类是否是 super 类的派生类
class A:pass
class B(A):pass
print(issubclass(B, A))
print(issubclass(A, B))

# 结果呈现
True
False

2 反射

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

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

  • 四个可以实现自省的函数,下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

  • hasattr

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
  • getattr
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
  • setattr
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
  • delattr
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
  • 四个方法的使用演示
# 反射:是用字符串类型的名字 去获取 变量
name = 1
eval("print(name)")     # 安全隐患

# 反射:没有安全问题
# 反射对象中的属性和方法
class A:
    def func(self):
        print("in func")

a = A()
a.name = "alex"
# 反射对象的属性
ret = getattr(a,"name")     # 通过变量名的字符串形式取到的值
print(ret)
print(a.__dict__)
variable_name = input(">>>")
# print(getattr(a, variable_name))
# print(a.__dict__[variable_name])


class A:
    def func(self):
        print("in func")

a = A()
a.name = "alex"
# 反射对象的方法
a.func()
ret = getattr(a,"func")
ret()


class A:
    price = 20
    @classmethod
    def func(cls):
        print("in func")

# 反射类的属性
# A.price
print(getattr(A,"price"))

# 反射类的方法: classmethod staticmethod
# A.func()
if hasattr(A,"func"):
    getattr(A, "func")()

# 模块
import test_project.my_module_obj
# 反射模块的属性
print(getattr(test_project.my_module_obj,"day"))

# 反射模块的方法
getattr(test_project.my_module_obj,"wahaha")()

# 内置模块
def qqxing():
    print("qqxing")

year = 2018

import sys
# print(sys.modules["__main__"].year)

# 反射自己模块中的变量
print(getattr(sys.modules["__main__"],"year"))

# 反射自己模块中的函数
getattr(sys.modules["__main__"],"qqxing")()

varible_name = input(">>>")
# print(getattr(sys.modules["__main__"], varible_name))
print(getattr(sys.modules[__name__], varible_name))

# 要反射的函数有参数怎么办
import time
print(time.strftime("%Y-%m-%d %H:%M:%S"))
print(getattr(time, "strftime")("%Y-%m-%d %H:%M:%S"))

# 一个模块中的类能不能反射得到
import test_project.my_module_obj
print(getattr(test_project.my_module_obj,"C")())



# setattr 设置修改变量
class A:
    pass
a = A()
setattr(A,"name","alex")
setattr(a,"name","nezha")
print(A.name)
print(a.name)

# delattr()
delattr(a,"name")
print(a.name)
delattr(A,"name")
print(A.name)
# my_module_obj.py
day = "Monday"  # 周一

def wahaha():
    print("wahahaha")

class C:
    pass

3 str__和__repr

  • 改变对象的字符串显示__str__,repr
  • 自定制格式化字符串__format__
format_dict={
    \'nat\':\'{obj.name}-{obj.addr}-{obj.type}\',#学校名-学校地址-学校类型
    \'tna\':\'{obj.type}:{obj.name}:{obj.addr}\',#学校类型:学校名:学校地址
    \'tan\':\'{obj.type}/{obj.addr}/{obj.name}\',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return \'School(%s,%s)\' %(self.name,self.addr)
    def __str__(self):
        return \'(%s,%s)\' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec=\'nat\'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School(\'myschool\',\'北京\',\'私立\')
print(\'from repr: \',repr(s1))
print(\'from str: \',str(s1))
print(s1)

\'\'\'
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
\'\'\'
print(format(s1,\'nat\'))
print(format(s1,\'tna\'))
print(format(s1,\'tan\'))
print(format(s1,\'asfdasdffd\'))
class A:
    def __str__(self):
        return "A\'s object"
    def func(self):
        return "wahaha"

a = A()
print(str(a))   # a.__str__  --> object
print(a)   # 答应一个对象的时候,就是调用a.__str__
# object 里有一个 __str__, 一旦被调用,就返回调用这个方法的对象的内存地址

lst = [1, 2, 3, 4, 5]   # 实例化一个列表类的对象
print(lst)

print(\'%s:%s\' % ("A", a))
# %s str()  直接打印实际上调的都是 __str__

class Teacher:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    def __str__(self):
        return "Teacher\'s object : %s" % self.name
    def __repr__(self):
        return str(self.__dict__)
    def func(self):
        return "wahaha"

nezha = Teacher("哪吒", 250)
print(nezha)
print(repr(nezha))
print(">>> %r" % nezha)
  • %r repr() 实际上调的是 repr
  • repr 是 str 的备胎,但str不能做repr的备胎
  • print(obj) / \'%s\'%obj / str(obj)的时候,实际上是内部调用了 obj.__str__方法,如果str方法有,那么它返回的必定是一个str数据类型
  • 如果没有__str__方法,会先找本类中的 __repr__方法,再没有再找父类的 str
  • repr(),只会找 repr, 如果没有找父类的 repr

4 del

  • 析构方法,当对象在内存中被释放时,自动触发执行。
  • 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class A:
    def __del__(self):      # 析构函数:在删除一个对象前进行一些收尾工作
        print("执行我啦")
a = A()
del a       # del 先执行了这个方法,又删除了变量 - - 执行我啦
print(a)    # NameError: name \'a\' is not defined

# 结果呈现
执行我啦
  File "C:/Users/thinkpad/Envs/daily_test/test_project/面向对象.py", line 1435, in <module>
    print(a)    # NameError: name \'a\' is not defined
NameError: name \'a\' is not defined
# 引用计数
class A:
    def __del__(self):      # 析构函数:在删除一个对象前进行一些收尾工作
        print("执行我啦")
a = A()
import time
time.sleep(3)   # 执行了del 方法 - - 执行我啦

# 结果呈现
执行我啦
class A:
    def __del__(self):      # 析构函数
        self.f.close()      # 在 a.f 在内存中消失前,先关闭打开的文件
a = A()
a.f = open()    # 打开文件 ,在操作系统中打开了一个文件,拿到了文件操作符存在了内存中
del a           # 删除 a 以后,a.f 拿到了的文件操作符也消失在内存中

5 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\']=\'alex\'
print(f1.__dict__)

6 new

  • 一个类 始终 只有 一个 实例
  • 当你第一次实例化这个类的时候,就创建一个实例化的对象
  • 当你之后再来实例化的时候,就用之前创建的对象
class A:
    def __init__(self):
        self.x = 1
        print(\'in init function\')
    def __new__(cls, *args, **kwargs):
        print(\'in new function\')
        return object.__new__(A, *args, **kwargs)

a = A()
print(a.x)
class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, \'_instance\'):
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

one = Singleton()
two = Singleton()

two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))
# 29097904
print(id(two))
# 29097904
print(one == two)
# True
print(one is two)

7 call

  • 对象后面加括号,触发执行。
  • 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class A:
    def __init__(self,name):
        self.name = name
    def __call__(self, *args, **kwargs):
        """
        打印这个对象中的所有属性
        :param args:
        :param kwargs:
        :return:
        """
        print("执行我啦")

        for k in self.__dict__:
            print(k,self.__dict__[k])

a = A("alex")
a()
# 等同于
a = A("alex")()

# 结果呈现
执行我啦
name alex
执行我啦
name alex

8 len

class Classes:
    def __init__(self, name):
        self.name = name
        self.student = []
    def __len__(self):
        return len(self.student)

py_ss = Classes("python全栈9期")
py_ss.student.append("二哥")
py_ss.student.append("泰哥")
print(len(py_ss))

# 结果呈现
2

9 hash

class A:
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
    def __hash__(self):
        return hash(self.name + self.sex)

a = A("agg", "男")
b = A("agg", "男")
print(hash(a))          # 2067652227830744138
print(hash(b))          # 2067652227830744138
c = A("agg", "女")
print(hash(c))          # 2005968340684947333

10 eq

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

obj1 = A("egg")
obj2 = A("egg")
print(obj1 == obj2)     # False

# 比较内存地址
class A:
    def __init__(self,name):
        self.name = name
    def __eq__(self, other):
        if self.name == other.name:
            return True
        else:
            return False

obj1 = A("egg")
obj2 = A("egg")
obj3 = A("egge")
print(obj1 == obj2)     # True
print(obj1 == obj3)     # False

11 例1 纸牌游戏

from collections import namedtuple

Card = namedtuple("Card", ["rank", "suit"])     # rank 牌面大小  suit牌面花色
# c1 = Card(2, "红心")
# print(c1)     # Card(rank=2, suit=\'红心\')
# print(c1.suit)    # 红心

class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list(\'JQKA\')
    suits = [\'红心\',\'方板\',\'梅花\',\'黑桃\']

    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits]       # 列表表达式 嵌套循环匹配获取所有牌

    def __len__(self):      # 获取牌数量
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

    def __setitem__(self, key, value):
        self._cards[key] = value

    def __str__(self):
        return json.dumps(self._cards,ensure_ascii=False)      # json 序列化

deck = FranchDeck()
print(deck[0])          # 第一张牌
from random import choice
print(choice(deck))     # 随机抽牌
print(choice(deck))     # 随机抽牌

from random import shuffle
shuffle(deck)       # 洗牌
print(deck._cards[:5])     # 获取前5 个
print(deck[:5])            # 获取前5 个   item方法

import json
print(deck)     # 查看所有牌面

12 例2 100个对象 名字和性别和年龄不同

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

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True


p_lst = []
for i in range(10):
    p_lst.append(Person(\'egon\',i,\'male\'))

print(p_lst)
print(set(p_lst))
# set 依赖对象的 hash eq

以上是关于Python - - 面向对象 - - 面向对象进阶的主要内容,如果未能解决你的问题,请参考以下文章

python-面向对象入门

Python-面向对象编程

Python面向对象之:三大特性:继承,封装,多态。

19.Python面向对象之:三大特性:继承,封装,多态。

《面向对象程序设计》高手进~~~~~~~~~~~~!!

为女儿准备的编程课 Lesson9: Python 面向对象设计