python ——面向对象进阶

1.staticmethod和classmethod

staticmethod  静态方法: 让类里的方法,直接被类调用,就像正常的函数一样

宝宝,男
博博,女
海娇,男
海燕,女
海东,男
海峰,男
class Student:
    # f = open('student', encoding='utf-8')
    def __init__(self):
        pass

    @staticmethod  
    def show_student_info():
        f = open('student', encoding='utf-8')  #静态方法不能和类属性直接交互,如果f放在第2行,f将作为类属性,那么下面的f将不能被调用
        for line in f:                         
            name, sex = line.strip().split(',')
            print(name, sex)
Student.show_student_info()    

classmethod  类方法: 默认参数cls,可以直接用类名调用,与类属性交互

class Student:
    f = open('student', encoding='utf-8')
    def __init__(self):
        pass
    @classmethod #类方法: 默认参数cls,可以直接用类名调用,与类属性交互
    def show_student_info(cls):
        for line in cls.f:
            name, sex = line.strip().split(',')
            print(name, sex)

Student.show_student_info()

classmethod和staticmethod的异同:

相同点:都可以直接被类调用,不需要实例化 不同点:     类方法(classmethod)必须有一个参数cls表示这个类,可以使用类属性     静态方法(staticmethod)不需要参数,但是静态方法不能直接使用类属性

2.绑定方法和非绑定方法

绑定方法:普通方法、类方法

  普通方法:默认有一个self对象传进来,并且只能被对象调用————绑定到对象   类方法:默认有一个cls传进来表示本类,并且可以被类和对象(不推荐)调用————绑定到类

非绑定方法:  

  静态方法:没有默认参数,并且可以被类和对象(不推荐)调用————非绑定

3.isinstance 和 issubclass

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo:
    pass
class Son(Foo):
    pass
s=Son()
print(isinstance(s,Son)) #True
print(isinstance(s,Foo)) #True
print(type(s) is Son) #False
print(type(s) is Foo)  #False
#isinstance和type的区别在于:当存在继承时,isinstance在判断时比较模糊,type则不会存在这个问题
print(issubclass(Son,Foo)) #True
print(issubclass(Son,object)) #True
print(issubclass(Foo,object)) #True
print(issubclass(int,object)) #True

4.反射

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

注:getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容。

四个可以实现自省的函数

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

常用:hasattr、getattr

不常用:setattr、delattr

class Foo:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say_hi(self):
        print('hi,%s'%self.name)

obj=Foo('egon',73)

#检测是否含有某属性
print(hasattr(obj,'name'))   #True
print(hasattr(obj,'say_hi'))  #True

#获取属性
n=getattr(obj,'name')
print(n)    #egon
func=getattr(obj,'say_hi')
func()     # hi,egon

# print(getattr(obj,'aaaaaaaa','不存在啊')) #报错

#设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)   #{'name': 'egon', 'age': 73, 'sb': True, 'show_name': <function <lambda> at 0x0097C5D0>}
print(obj.show_name(obj))   #egonsb    
#用setattr为对象设置方法时:需要在()内手动传入绑定的对象,不能直接obj.show_name(),会报错

#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,则报错

print(obj.__dict__)   #{'name': 'egon', 'sb': True}

在对象中应用反射

class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def func(self):
        print(123)

egg=Foo('egon',73)
print(egg.name)   #egon
print(egg.__dict__) #可以查看类的属性,不能查看方法   #{'name': 'egon', 'age': 73}
print(egg.__dict__['name'])  #egon


# 常用
# hasattr  判断对象是否存在某属性或方法
# getattr  如果存在这个方法或者属性,就返回属性值或者方法的内存地址;如果不存在,报错

print(hasattr(egg,'name'))  #True
print(getattr(egg,'name'))  #egon
print(hasattr(egg,'func'))  #True
print(getattr(egg,'func'))  #<bound method Foo.func of <__main__.Foo object at 0x00E2FBF0>>


if hasattr(egg,'func'):   #返回bool
    Foo_func=getattr(egg,'func')  #如果存在这个方法或者属性,就返回属性值或者方法的内存地址
                                #如果不存在,报错,因此要配合hasattr使用
    Foo_func()     #123

在类中应用反射

class Foo:
    f=123  #类变量

    @classmethod
    def class_method_demo(cls):
        print('class_method_demo')

    def class_method_demo1(self):
        print('class_method_demo1')

if hasattr(Foo,'f'):
    print(getattr(Foo,'f'))

print(hasattr(Foo,'class_method_demo'))  #True
print(hasattr(Foo,'class_method_demo1'))  #True
method=getattr(Foo,'class_method_demo')
method1=getattr(Foo,'class_method_demo1')
method()  #class_method_demo
method1() #TypeError: class_method_demo1() missing 1 required positional argument: 'self'

#类也是对象

在模块中应用反射 : 本模块和导入的模块

def test():
    print('test')
import  my_module
print(hasattr(my_module,'test'))  #True
func_test=getattr(my_module,'test')
func_test() #test
getattr(my_module,'test')() #test   #和上面注释掉的2行等价
def demo1():
    print('demo1')
import sys
# print(sys.modules) # 一个模块字典,
# print(__name__)  #__main__
# print(sys.modules[__name__])  #返回本py文件里的模块  <module '__main__' from 'E:/PycharmProjects/untitled/8.16/6反射3.py'>
module_obj=sys.modules[__name__] #sys.modules[__main__]
print(hasattr(module_obj,'demo1'))  #True    #在本模块中找函数demo1
getattr(module_obj,'demo1')()      #demo1

5  __str__,__repr__

先来一段代码,再来引入我们的正题:

lst = list([1,2,3,4])
class Foo:
    def __init__(self,name):
        self.name = name

f = Foo('xiaohua')

print(lst)
print(f)

######输出结果#########
[1, 2, 3, 4]
<__main__.Foo object at 0x0000005FE8BC9208>

我们都知道在python里面,list是一种数据类型,而且也是一个类。当我们以list()来创建一个序列时,我们也就创建了一个List对象。如上面的lst,知道了lst是List类型的对象后,我们print(lst)时,为什么没有像f一样,打印出对象的内存地址,而是直接打印出给对象赋的值呢?其实,这一切是因为在list类的内部,实现了__str__功能所导致的。下面,我们通过一些实例,来理解__str__的用法。

class Foo:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return '我执行了'

f = Foo('xiaohua')
print(f)

#######输出结果############
我执行了

看,当我再次打印对象的时候,没有打印出对象的内存地址,而是打印出了我们设定的值。__str__就是帮我们实现这种功能的!我们可以定制自己__str__,让他返回一些有意义的信息。

def __str__(self):
        return 'name:%s' % self.name

  print(f)

  ####输出结果#####
  name:xiaohua
class Foo:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return '%s obj info in str'%self.name
    def __repr__(self):
        return 'obj info in repr'

f = Foo('egon')
print(f)    # egon obj info in str
print('%s'%f)   # egon obj info in str
print('%r'%f)    #obj info in repr
print(repr(f))  #repr(f)等价于f.__repr__()   # obj info in repr
print(str(f))   #str(f)等价于f.__str__()    # egon obj info in str

__str__和__repr__必须return 字符串 当打印一个对象的时候,如果实现了str,打印__str__中的返回值,当__str__没有被实现的时候,就会调用__repr__方法(即__str__和__repr__同时存在时,会执行str,不会执行repr) 但是当你用字符串格式化的时候 %s和%r会分别去调用__str__和__repr__ 不管是在字符串格式化的时候还是在打印对象的时候,repr方法都可以作为str方法的替补 但反之不行 用于友好的表示对象。如果str和repr方法你只能实现一个:先实现repr

6 __new__,__del__

__new__:创建对象时被调用,比__init__先执行。

class Foo(object):
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new %s' %cls)
        return object.__new__(cls, *args, **kwargs)
Foo()

###输出结果#######
new <class '__main__.Foo'>  # 比init先打印
init

 从上面,我们可以总结出如下几点:   

    继承自object的新式类才有__new__

    __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

    __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

    __init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行

class A(object):
    pass

class B(A):
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new %s' % cls)
        return object.__new__(A, *args, **kwargs)
b = B()
print(type(b))

输出结果:
new <class '__main__.B'>
<class '__main__.A'>
class A:
    def __init__(self):  #有一个方法在帮你创造self
        print('in init function')
        self.x = 1

    def __new__(cls, *args, **kwargs):
        print('in new function')
        return object.__new__(cls, *args, **kwargs)
a = A()
b = A()
c = A()
print(a,b,c)

################单例模式###############
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()
three = Singleton()
print(one,two,three)

one.name = 'alex'
print(two.name)

######结果如下########
in new function
in init function
in new function
in init function
in new function
in init function
<__main__.A object at 0x0297F6F0> <__main__.A object at 0x0297FB50> <__main__.A object at 0x0297FC10>
<__main__.Singleton object at 0x0297FC70> <__main__.Singleton object at 0x0297FC70> <__main__.Singleton object at 0x0297FC70>
alex

__del__:在对象被删除或者程序执行完毕后,被调用

# 以下代码执行完毕后,自动执行del
class B:
    def __del__(self):
        print('我执行了')
b = B()

######输出结果#########
我执行了
#显式调用del来删除对象时,触发__del__执行
import time
class B:
    def __del__(self):
        print('我执行了')
b = B()
del b # 触发__del__执行
time.sleep(5)
print('主程序结果')

###输出结果####
我执行了
#等待5秒
主程序结果

7.item系列

__getitem__\__setitem__\__delitem__

我们在列表中学过这种取元素的方式。比如说lst = [1,2,3,4],取第一个元素 lst[0],又或者在字典中dd ={'name':'xiaohua'} 取元素dd['name']。实际上,这种取元素的方式,与__getitem__,__setitem__,__delitem__三个函数有关。

  下面,我们一一来看看上述三个函数的用法:

  __getitem__:当我们想要按照 obj[attr]方式调用对象的属性时,触发这个函数的执行

class Foo:
    def __init__(self,name):
        self.name = name
    def __getitem__(self, item):
        print('我执行了')

f = Foo('alex')
print(f.__dict__) # 对象的名称空间只有一个属性
f['name']  # 触发getitem执行

#####输出结果########
{'name': 'alex'}
我执行了

从上面结果,可以看出,当我试图以f['name']方式调用属性的时候,就会触发__getitem__执行。一般将__getitem__设置为如下:

class Foo:
    def __init__(self,name):
        self.name = name
    def __getitem__(self, item):
            print(self.__dict__[item])

f = Foo('alex')
print(f.__dict__)
f['name']
########输出结果#######
{'name': 'alex'}
alex

__setitem__:当我以f['name'] = 'xiaohua' 方式修改对象属性值的时候,会触发该函数的执行。

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

    def __setitem__(self, key, value):
        print('我执行了')

f = Foo('alex')
print(f.__dict__)
f['x'] = 3  # 触发setitem执行

#########输出结果#########
我执行了

一般将setitem设置为如下,当然你也可以按照自己的方式进行设置。

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

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

f = Foo('alex')
print(f.__dict__)
f['x'] = 3
print(f.__dict__)

###输出结果#######
{'name': 'alex'}
{'name': 'alex', 'x': 3}

__delitem__:当以del f['name'] 方式删除对象的属性值时,会触发這个函数的执行

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

    def __delitem__(self, key):
        print('我执行了')


f = Foo('alex')
print(f.__dict__)
del f['name']  # 触发delitem执行

####输出结果#####
{'name': 'alex'}
我执行了

一般设置为如下方式:

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

    def __delitem__(self, key):
        del f.__dict__[key]


f = Foo('alex')
print(f.__dict__)
del f['name']
print(f.__dict__)
#########输出结果####

8__call__

对象后面加括号,触发执行。

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

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__   Foo()()等价于obj()

9 __len__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
obj = A()
print(len(obj))

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python爱好者

正则表达式基础

1777
来自专栏王磊的博客

JS性能优化

下面是一些关于客户端JS性能的一些优化的小技巧: 1.关于JS的循环,循环是一种常用的流程控制。JS提供了三种循环:for(;;)、while()、for(in...

5648
来自专栏黑泽君的专栏

c语言基础学习07_指针

=============================================================================

1800
来自专栏codingforever

经典算法巡礼(二) -- 排序之选择排序

选择排序,如冒泡排序一样,从名字中即可大概猜测其排序的原理。其工作原理就是从未排序的数组中选出最大(小)的元素,将其放置至数组的首(尾)部,重复此过程直至没有未...

371
来自专栏java一日一条

Java内部类

这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静...

911
来自专栏Python数据科学

Python 内建函数大全

Python 解释器内置了许多函数和类型,列表如下(按字母排序)(省略了几个我没用过或者不常用的)。

2353
来自专栏谈补锅

C语言之字符、整数、数组、字符串笔记

每种类型占用内存空间不一样,比如char占一个字节,short占2个字节,int占4个字节,double占8个字节

4583
来自专栏liulun

Nim教程【八】

有序类型 值连续的枚举类型、整型、字符类型、布尔类型(还有这些类型的变种), 都可以称之为有序类型,Nim为有序类型提供了一系列特殊的方法 方法签名 方法...

2056
来自专栏mukekeheart的iOS之旅

OC学习2——C语言特性之函数

1、OC是在C语言的基础上进行扩展的,在OC中直接用C语言进行coding也是可以通过编译的。因此,函数定义的语法格式如下: 函数返回值类型 函数名(形参列表...

3107
来自专栏菩提树下的杨过

js中数组(Array)的排序(sort)注意事项

直接看代码吧,测试结果也贴在里面了 var arrDemo = new Array(); arrDemo[0] = 10; arrDemo[1] = 50...

2926

扫码关注云+社区

领取腾讯云代金券