当前位置 : 主页 > 编程语言 > python >

Python--面向对象进阶

来源:互联网 收集:自由互联 发布时间:2022-06-15
isinstance和issubclass isinstance isinstance(obj1,obj2):判断对象与类之间的关系,判断第一个参数是否是第二个参数的实例。 n1 = 10 isinstance(n1, int) #判断n1是否是数字类型,如果是返回True如果不

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

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

getattr()

Python--面向对象进阶_PythonPython--面向对象进阶_实例化_02

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

具体使用:

  (1)getattr(obj,属性名)----> 传入字符串类型的属性名,返回属性的值,相当于执行了“obj.属性名”

  (2)getattr(obj,方法名)----> 传入字符串类型的方法名,返回对应方法的内存地址。

  谨记:如果第二个参数不存在的话,就会报错。

class A:
name = "小白"
age = 18

def func(self):
print("in the func")

print(A.name)
print(A.age)

# 使用反射的形式,操作类的属性
print(getattr(A, 'name'))

# 使用反射操作类中的方法
a = A()
ret = getattr(a, 'func') # 得到了func方法的内存地址
ret() # 调用func()方法

hasattr()

Python--面向对象进阶_PythonPython--面向对象进阶_实例化_02

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

具体使用: 

  hasattr(obj,字符串类型的属性名/方法名)--->判断对象中是否拥有指定属性/方法,返回True/False。

  谨记:所以hasattr经常和getattr一起使用,先用hasattr判断,再使用getattr取值。

# 例子1:通过hasattr判断,再用getattr来取值。
class A:
name = "xiao"
age = 18

inpu = input('>>>: ')
if hasattr(A, inpu):
print(getattr(A, inpu))
else:
print("A类中没有对应的属性")

>>>: name
xiao
>>>: sex
A类中没有对应的属性

# 例子2:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def show(self):
for key in self.__dict__: #__dict__查看对象的所有属性
print(key, self.__dict__[key]) # key self.__dict__[key]

stu1 = Student("小白", 18)
if hasattr(stu1, 'show'):
func = getattr(stu1, 'show') # 返回方法的内存地址,赋值给func()
func()

name 小白
age 18

setattr()

Python--面向对象进阶_PythonPython--面向对象进阶_实例化_02

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

具体使用:

  setattr(obj,'name',value) ---->通过反射的方式为一个对象设置属性(增改操作),相当于obj.name=value

# 正常情况下,想给对象设置一个值。
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def show(self):
for key in self.__dict__:
print(key, self.__dict__[key])

stu1 = Student("小白", 18)
stu1.name = "小黄"
print(stu1.name) # 小黄


# 除了上面的方式之外,还有一种方式为对象设置属性,通过反射的形式.
setattr(stu1, 'name', '菜农')
print(stu1.__dict__) # {'name': '菜农', 'age': 18}
print(stu1.name) # 菜农

除了给对象绑定属性,还可以对象绑定一个函数。

# 创建一个函数,通过setattr给对象绑上这个函数
class Student:
def __init__(self, name, age):
self.name = name
self.age = age

def show(self):
for key in self.__dict__:
print(key, self.__dict__[key])

stu1 = Student("小白", 18)

def func():
print('In func')

setattr(stu1, 'func1', func)
stu1.func1() # In func
print(stu1.__dict__) # {'name': '小白', 'age': 18, 'func1': <function func at 0x00000226123740D0>}

delattr()

Python--面向对象进阶_PythonPython--面向对象进阶_实例化_02

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

具体使用:

  delattr(obj,'name'):通过反射的方式,删除对象中的属性(删除操作)

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

def show(self):
for key in self.__dict__:
print(key, self.__dict__[key])


stu1 = Student("小白", 18)
# 正常方法
del stu1.name
print(stu1.__dict__) # {'age': 18}

# 通过delattr反射的方式
delattr(stu1, 'name')
print(stu1.__dict__) # {'age': 18}

类使用反射

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

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')

自定义模块使用反射

既然反射能够操作模块,那么当前文件其实也是一个模块,通过sys.modules可以看出,当前文件就是sys.modules['__main__']。

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')()

通过sys.modules['__main__']好像完成了要求,但是,仔细想一下,使用__main__表示当前文件,假如在另外一个py文件中把当前文件导入,此时__main__便指向的是另外一个py文件,使用sys.modules['__main__']这种方式显然是不妥的,那么要怎么解决?

使用sys.modules[__name__]就能完美解决,因为__name__就是'__main__'。

#通过sys.modules[__name__]反射当前模块<br>import sys
import sys
def func():
print("本模块函数func")

getattr(sys.modules[__name__], 'func')() # 本模块函数func

  总结:

  sys.modules['__main__'] : __main__会随着文件的不同而不同,存在安全隐患。

  sys.modules[__name__]:__name__不管在哪里导入这个模块,都代表这个文件。

反射总结

  (1)hasattr():判断一个对象中是否能够调用一个名字,返回True/False

  (2)getattr():返回一个对象中的名字的值

  (3)setattr():为一个对象设置属性(增加/修改操作)

  (4)delattr():删除一个对象的某个属性(删除操作)

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

反射的应用

Python--面向对象进阶_PythonPython--面向对象进阶_实例化_02

#! /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

 扩展小知识:

# 一个类
# 对象的属性 : 姓名 性别 年龄 部门
# 员工管理系统
# 内部转岗 python开发 - go开发
# 姓名 性别 年龄 新的部门
# alex None 83 python
# alex None 85 luffy

# 1000个员工
# 如果几个员工对象的姓名和性别相同,这是一个人
# 请对这1000个员工做去重


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

def __hash__(self):
return hash('%s%s' % (self.name, self.sex))

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


employ_list = []
for i in range(200):
employ_list.append(Employee('小白', i, 'male', 'python'))

for i in range(200):
employ_list.append(Employee('菜鸟', i, 'male', 'python'))

for i in range(200):
employ_list.append(Employee('老鸟', i, 'male', 'python'))

employ_set = set(employ_list)
for person in employ_set:
print(person.__dict__)

'''
打印结果:
{'name': '小白', 'age': 0, 'sex': 'male', 'partment': 'python'}
{'name': '菜鸟', 'age': 0, 'sex': 'male', 'partment': 'python'}
{'name': '老鸟', 'age': 0, 'sex': 'male', 'partment': 'python'}
'''

# set集合的去重机制 : 先调用hash,再调用eq,eq不是每次都触发,只有hash值相等的时候才会触发


人生是条无名的河,是浅是深都要过; 人生是杯无色的茶,是苦是甜都要喝; 人生是首无畏的歌,是高是低都要唱。


上一篇:Python运维编程(1)环境部署
下一篇:没有了
网友评论