草庐IT

面向对象高级--反射、内置方法和元类

suncolor 2023-04-19 原文

1.反射

1.1什么是反射

python是一门动态语言,而反射机制被视为动态语言的关键!

反射机制指的是:在程序的运行过程中,动态的获取程序的信息和对象的功能!

‘动态’:指一开始不知道程序的信息和对象的功能,只有等到运行到那的时候才会动态获取!!!

比如:x=18
在程序运行的时候,python才会通过反射机制动态的获取到这个值是整型,并不需要一开始定义的时候,就规定这个18位整型!

1.2为什么要用反射机制

当我们获取一个对象时,很多场景下,我们是并不知道这个对象里面是有什么属性和方法的,我们需要通过反射机制,动态的获取到该对象的属性和方法!!

案例:当我们在接收用户输入指令的时候,我们接收的是一个用户输入的字符串类型的指令,我们需要通过反射机制判断,这个字符串是不是该对象的功能,如果是调用该功能,如果不是返回提示信息!

案例代码化:

class Func:
    def put(self):
        print('正在执行上传功能')

    def get(self):
        print('正在执行下载功能')

    # 该函数是用来接收用户的操作指令,并判断该对象是否有该功能,有则执行,没有则提示没有该功能
    def action(self):
        action = input('请输入操作指令:')
        if hasattr(self,action):
            getattr(self,action)()
        else:
            print('没有该功能')
obj = Func()
obj.action()

1.3如何实现反射机制

其实就是四个内置函数的使用!

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

obj = People('zhang')

# 1.可以通过dir方法获取obj对象有哪些属性
print(dir(obj)) # 格式是一个列表套字符串的形式

# 2.通过字符串反射到真正的属性上,从而得到属性,操作属性
# 四个内置函数的使用
print(hasattr(obj,'name'))  # hasattr()判断obj这个对象有没有name这个属性,name是字符串格式
print(getattr(obj,'name'))  # 等同于obj.name
setattr(obj,'name','yang') # 等同于obj.name='yang'
delattr(obj,'name')  # 等同于 del obj.name
print(obj.__dict__)  # 结果为{}
# 上述四个方法也可以括号里放个类,判读类是否有该函数
res = getattr(People,'say')  # 等同于People.say
print(res)

2.内置方法

2.1什么是内置方法

定义在类的内部,以__开头__结尾的方法

特点是在满足某种情况下自动触发该方法!!!!

2.2为毛要用内置方法

为了自定义定制我们的类or对象

2.3如何使用内置方法

2.3.1 __str__方法

class Func:
    def put(self):
        print('正在执行上传功能')
    def __str__(self):
        return 'w1e' # 打印对象是返回的值,必须位字符串类型
obj = Func()
print(obj)  # 等同于print(obj.__str__())
# 不定义__str__方法,印出来为<__main__.Func object at 0x00000149DE206FA0>
# 定义__str__方法,可以在__str__内部函数指定返回的东西,return后面必须是字符串类型

2.3.2 __del__方法

该方法是在清理对象之前触发,会先执行该方法

class Func:
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')
        # 在del内部更多的是进行清理该对象占用系统的资源,对象清理了需要发起系统调用,清理对象占据的系统资源!
obj = Func()
del obj # 清理对象了,清理完之后会直接调用__del__方法,然后在执行下面的代码;如果不手动清理,在执行代码全部运行完之后,程序也会清理,打印run
print('====')

2.3.3 __call__方法

如果想要让一个对象可以加括号调用,需要在该对象的类中添加一个__call__方法

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

    def __call__(self, *args, **kwargs):
        print(args,kwargs) #a()里传入的参数
        return 123  # __call__的返回值就是a()的返回值

a = Person('zhang')
res = a(1,2,3,a=4,b=5) # 如果想要让对象a可以加括号调用,就必须在该对象的类中定义__call__方法,不添加则报错
print(res)

同样,如果想要类可以加括号调用,需要在该类的元类里添加一个__call__方法

调用__call__方法完成了三件事:
1.调用该类中的__new__方法造出一个空对象
2.调用该类中的__init__方法造出一个初始化对象(给上面的空对象添加属性,穿衣服)
3.返回初始化好的对象

2.4 总结

了解:这些内置方法__str__等又称魔法方法!

# __init__:类实例化会触发
# __str__:打印对象会触发
# __call__:对象()触发,类也是对象  类(),类的实例化过程调用元类的__call__
# __new__:在类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服)
# __del__:del 对象,对象回收的时候触发
# __setattr__,__getattr__:(.拦截方法),当对象.属性--》赋值会调用setattr,如果是取值会调用getattr
# __getitem__,__setitem__:([]拦截)
# __enter__和__exit__ 上下文管理器

上下文管理器应用:

class Person:
    def __enter__(self):
        print("我在with管理的时候,会触发")
        print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
        return 'oo'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('退出with代码块时执行此方法')
        print('1', exc_type)
        print('2', exc_val)
        print('3', exc_tb)


with Person() as p:   # 这句话执行,会触发类的__enter__
    print(p)

小练习:类中可以通过对象.属性的方式获取值和设置值,但是不可以通过对象['键']=值设置值和获取值,思考如何可以实现呢?

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

    def __setitem__(self, key, value): # 重写类的__setitem__方法
        setattr(self,key,value) # 当对象['键']=值触发该方法,调用setattr是触发self.key=value

    def __getitem__(self, item): # 同上
        return getattr(self,item)

p = Person('zy')
print(p.name)
p['name']=10
print(p['name'])

3.元类

3.1什么是元类

元类就是用来实例化产生类的类

关系:元类--->实例化--->类--->实例化--->对象(obj)

3.2如何查看内置的元类

其实,我们使用class定义的各种类和内置的类都是由内置的元类type帮我们实例化产生的

我们可以使用type()函数查看内置的元类

例如:在python中int、dict内置元类都继承自object类,int和dict又都是type元类的对象

print(type(int))  # <class 'type'>
print(type(dict)) # <class 'type'>

那么type和object又是什么关系呢?我们来type一下object和type!

print(type(type))  #<class 'type'>
print(type(object)) #<class 'type'>

其实:

1.object的元类其实是type类,object是由type类构造出来的对象
2.type是自己的对象(指针指向了自己)
3.type类又继承了object类

3.3class机制(class如何造出类的)

calss其实底层执行了以下四个步骤,造出了类!

class Func:
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')
# 1.得到类名
class_name = 'Func'

# 2.得到类的基类
class_bases = (object,)

# 3.执行类体代码拿到名称空间!
class_dict = {}
class_body = """
def put(self):
    print('正在执行上传功能')
def __del__(self):
    print('run...')
"""
# exec第一个参数是类体代码、第二个是类体代码中的全局变量、第三的是一个空字典容器
exec (class_body,{},class_dict)
print(class_dict)

# 4.调用元类,得到People类
Func = type(class_name,class_bases,class_dict)
print(Func)

# Func类就是type元类实例化产生出来的对象!!!!

3.4如何自定义元类来控制类的产生

在3.3中,我们是使用type元类控制Func类的产生。其实,我们也可以自定义元类来控制类产生

class MyMeta(type):   # 只有继承了type的类才是元类
    def __init__(self,x,y,z): # 注意调用MyMeta这个类其实传入了四个参数分别是self、class_name,class_bases,class_dict
        print('run...')
        print(x) # x对应class_name
        print(y) # y对应class_bases
        print(z) # z对应class_dict

class Func(metaclass=MyMeta):
    def put(self):
        print('正在执行上传功能')
    def __del__(self):
        print('run...')

# 在自定义类的时候,metaclass默认等于type,我们可以通过指定metaclass=MyMeta来自定义元类
# 指定了metaclass=MyMeta,其实就执行了第四步调用元类Func = MyMeta(class_name,class_bases,class_dict)
# 调用MyMeta(class_name,class_bases,class_dict)发生了三件事!
# 注意!!!调用它就等于调用了type的__call__方法!!!!
#     1.先造一个空对象---Func--这里其实先调用了MyMeta类里的__new__()方法
#     2.调用MyMeta这个类的__init__方法,完成初始化对象操作
#     3.返回初始化好的对象

"""
完成上述操作之后,我们就可以在自定义的MyMeta元类里面的init方法里面,规定一下类的产生必须满足那些条件!
"""

3.5 元类下的属性查找

首先,切记!!父类不是元类!!

对象.属性查找是先从自己那找,再到类中,再到该类的父类中,最后到object类

类.属性查找是先从该类的父类中找,再到父类的父类中找,再到object中找,最后还要到该类的元类中找

有关面向对象高级--反射、内置方法和元类的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  5. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  6. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  8. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  9. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐