day7--面向对象进阶(内含反射和item系列)

一面向对象的结构和成员

1.1面向对象的结构

class A:

    company_name = '老男孩教育'  # 静态变量(静态字段)
    __iphone = '1353333xxxx'  # 私有静态变量(私有静态字段)


    def __init__(self,name,age): #普通方法(构造方法)

        self.name = name  #对象属性(普通字段)
        self.__age = age  # 私有对象属性(私有普通字段)

    def func1(self):  # 普通方法
        pass

    def __func(self): #私有方法
        print(666)


    @classmethod  # 类方法
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod  #静态方法
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')

    @property  # 属性
    def prop(self):
        pass
View Code

1.2面向对象的私有和公有

对于静态字段(静态变量),普通字段(对象属性),方法

公有:类可以访问,类内部可以访问,派生类可以访问

私有:仅类内部可以访问

class C:

    name = "公有静态字段"

    def func(self):
        print C.name

class D(C):

    def show(self):
        print C.name


C.name         # 类访问

obj = C()
obj.func()     # 类内部可以访问

obj_son = D()
obj_son.show() # 派生类中可以访问
公有静态字段
class C:

    __name = "私有静态字段"

    def func(self):
        print C.__name

class D(C):

    def show(self):
        print C.__name


C.__name       # 不可在外部访问

obj = C()
obj.__name  # 不可在外部访问
obj.func()     # 类内部可以访问   

obj_son = D()
obj_son.show() #不可在派生类中可以访问 
私有静态字段
class C:

    __name = "私有静态字段"

    def func(self):
        print C.__name

class D(C):

    def show(self):
        print C.__name


C.__name       # 不可在外部访问

obj = C()
obj.__name  # 不可在外部访问
obj.func()     # 类内部可以访问   

obj_son = D()
obj_son.show() #不可在派生类中可以访问 
公有普通字段
class C:
    
    def __init__(self):
        self.__foo = "私有字段"

    def func(self):
        print self.foo  # 类内部访问

class D(C):
    
    def show(self):
        print self.foo # 派生类中访问

obj = C()

obj.__foo     # 通过对象访问    ==> 错误
obj.func()  # 类内部访问        ==> 正确

obj_son = D();
obj_son.show()  # 派生类中访问  ==> 错误
私有普通字段
class C:

    def __init__(self):
        pass
    
    def add(self):
        print('in C')

class D(C):

    def show(self):
        print('in D')
        
    def func(self):
        self.show()
obj = D()
obj.show()  # 通过对象访问   
obj.func()  # 类内部访问    
obj.add()  # 派生类中访问  
公有方法
class C:

    def __init__(self):
        pass

    def __add(self):
        print('in C')

class D(C):

    def __show(self):
        print('in D')

    def func(self):
        self.__show()
obj = D()
obj.__show()  # 通过不能对象访问
obj.func()  # 类内部可以访问
obj.__add()  # 派生类中不能访问
私有方法

1.3面向对象的成员

1 字段

class Province:

    # 静态字段
    country = '中国'

    def __init__(self, name):

        # 普通字段
        self.name = name


# 直接访问普通字段
obj = Province('河北省')
print obj.name

# 直接访问静态字段
Province.country
View Code

上面的代码可以看出:普通字段需要通过对象来访问  , 静态字段需要通过类访问

内存中存储方式:

静态字段在内存中子保存一份,实例化对象后,访问静态字段都指向类中的这个内存地址

普通字段在每个对象中都要保存一份

2方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  • 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
  • 静态方法:由调用;无默认参数;
class Foo:

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

    def ord_func(self):
        """ 定义普通方法,至少有一个self参数 """

        # print self.name
        print '普通方法'

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
           return cls('xingchen') 

    @staticmethod
    def static_func(): #设置不依赖于类和函数,不需要它们传self或者cls
        """ 定义静态方法 ,无默认参数"""

        print '静态方法'
    @staticmethod
    def static_func2(n): #可以设置为传递一个参数
        print('n')
    


# 调用普通方法
f = Foo()
f.ord_func()

# 调用类方法
Foo.class_func()
Foo.name #可以打印出xingchen

# 调用静态方法
Foo.static_func()     

f.static_func2(1)   
Foo.static_func2(2)   
View Code

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

3属性

什么是property

它是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('egon',75,1.85)
print(p1.bmi)  #本来应该时p1.bmi()
View Code

为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价
View Code

 二面向对象的内置函数

1 isinstance和issubclass

isinstance() 与 type() 区别:
  type() 不会认为子类是一种父类类型,不考虑继承关系。
  isinstance() 会认为子类是一种父类类型,考虑继承关系。

#isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class A:pass
class B(A):pass

abj=B()
print(isinstance(abj,B)) #True
print(isinstance(abj,A)) #True

#issubclass(sub, super)检查sub类是否是 super 类的派生类
print(issubclass(B,A)) #True
View Code

2 反射

反射:是通过字符串的形式去操作对象相关的属性

 I对实例化对象的反射

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

obj=Foo('xincheng',18)
print(obj.name) #xincheng
print(hasattr(obj,'name')) #True 检查是否含有name的属性
#获取
my_name=getattr(obj,'name') #获取某个对象的属性
print(my_name) #xincheng
func=getattr(obj,'say_hi') #获取某个对象的属性
func() #my name is:  xincheng
#报错设置
# print(getattr(obj,'aa')) #报错:AttributeError: 'Foo' object has no attribute 'aa'
print(getattr(obj,'aa','没有此属性')) #可以设置自己需要的报错说明 没有此属性
#增加或修改
setattr(obj,'sb','hehe')
print(obj.__dict__) #{'name': 'xincheng', 'age': 73, 'sb': 'hehe'}
print(obj.sb) #hehe
setattr(obj,'show_name',lambda self:self.name+'sb') #此属性相当于设置了一个函数
print(obj.__dict__) #{'name': 'xincheng', 'age': 73, 'sb': 'hehe', 'show_name': <function <lambda> at 0x0000027089CA6048>}
print(obj.show_name(obj)) #xinchengsb
#删除
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show') #没有此属性报错,不能设置报错说明
View Code

II对类的反射

class Foo(object):
    staticField = "old boy"

    def __init__(self):
        self.name = 'wupeiqi'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'


print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))
#结果为:
# old boy
# <function Foo.func at 0x000002903EE65730>
# <function Foo.bar at 0x000002903EE657B8>
func=getattr(Foo,'bar')
print(func) ## <function Foo.bar at 0x000002903EE657B8> 内存地址和类的bar方法一样
View Code

III对当前模块

def login():
    print(888)

msg=input('>>>>').strip()
import sys
print(sys.modules)
this_module=sys.modules[__name__]
print(this_module) #<module '__main__' from 'G:/day8/练习.py'>

print(hasattr(this_module,'login')) #True
if hasattr(this_module,msg):
    getattr(this_module,msg)()
else:
    print('没有此属性')
View Code

IIII对另外一个模块

#模块名称模块测试反射,内容如下
def test():
    print('来自于另外一个模块')

#本地操作:
import 模块测试反射 as obj
print(hasattr(obj,'test'))
getattr(obj,'test')()
View Code

 3 __len__

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

    def __len__(self):
        return len(self.__dict__)
a = A()
print(a.__dict__) #{'a': 1, 'b': 2}
print(len(a)) #2 使用len函数的时候它计算的是实例化对象字典的长度
View Code

4 __hash__

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

    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a)) #这个hash值刷新一次改变一次
View Code

5 __str__和__repr__

如果一个类定义了__str__方法,那么在打印的时候,默认触发该方法

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 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('oldboy1','北京','私立')
print('from repr: ',repr(s1))  #from repr:  School(oldboy1,北京)
print('from str: ',str(s1))  #from str:  (oldboy1,北京)
print(s1) #(oldboy1,北京) 默认触发str方法

'''
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'))
#结果为:
# oldboy1-北京-私立
# 私立:oldboy1:北京
# 私立/北京/oldboy1
# oldboy1-北京-私立
View Code

打印类型的触发

class B:

    def __str__(self):
        return 'str : class B'

    def __repr__(self):
        return 'repr : class B'


b = B()
print('%s' % b)  #str : class B
print('%r' % b)  #repr : class B
View Code

6 __call__

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

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

class Foo:

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

        print('__call__')


obj = Foo() # 执行 __init__
obj()       # 执行 __call__ #打印出 __call__
View Code

 7 __del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo:
    def __del__(self):
        print('del----->')

obj=Foo()
print('hehe!')
'''
结果为:
hehe!
del----->
这种情况是等待这个py的脚本完成执行的时候,触发del命令
'''
#如果换一种情况呢
class Moo:
    def __del__(self):
        print('del----->')

oby=Moo()
del oby #提前触发 del命令
print('hehe!')
'''
结果为:
del----->
hehe!

'''
View Code

 8 __new__

此方法在实例化的时候触发,它的调用在__init__之前发生,object默认就有此方法

先讲一个模式:单例模式   --》就是一个类只能有一个实例

应用场景

1.当一个类,多次实例化时,每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次

2.当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题

情况一:一般的情况

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()
'''
in new function  #触发__new__,在__init__之前
in init function
'''
print(a.x) # 1

b=A() # 结果和a一样
View Code

情况二:单例模式

class B:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            obj = object.__new__(cls)
            cls.__instance = obj
        return cls.__instance

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

    def func(self):
        print(self.name)


a = B('alex', 80)  # 实例化,传值
print(a.name)
b = B('egon', 20)  # 实例化,覆盖值
print(a)
print(b)
print(a.name)
print(b.name)
'''
alex
<__main__.B object at 0x0000023A1D0B8BA8>
<__main__.B object at 0x0000023A1D0B8BA8>
egon
egon
它们内存地址一样,这是个单例模式的例子
b实例化的时候,传的值覆盖了a
'''
单例模式

 9 __eq__

使用==的时候触发

10 item系列

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

    def __getitem__(self, item):
        print('getitem----->')
        return self.__dict__[item]

    def __setitem__(self, key, value):
        print('setitem----->')
        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')
print(f1['name']) #触发 getitem方法
f1['age']=18 #触发setitem方法
f1['age1']=19 #触发setitem方法
del f1.age1   #触发 delattr方法
del f1['age'] #触发 delitem方法
f1['name']='alex' #触发setitem方法
# print(f1.__dict__)

'''
执行结果为:
getitem----->
sb
setitem----->
setitem----->
del obj.key时,我执行
del obj[key]时,我执行
setitem----->
{'name': 'alex'}
'''
View Code

 11 最后来看一个实例,关于hash和eq

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)
yy=set(p_lst)
print(yy)
'''
结果为:
[<__main__.Person object at 0x00000152982D8DD8>, <__main__.Person object at 0x00000152982D8E10>, <__main__.Person object at 0x00000152982D8EB8>, <__main__.Person object at 0x00000152982E1EB8>, <__main__.Person object at 0x00000152982E1E80>,
 <__main__.Person object at 0x00000152984770F0>, <__main__.Person object at 0x00000152984774E0>, <__main__.Person object at 0x0000015298477518>,<__main__.Person object at 0x0000015298477550>, <__main__.Person object at 0x0000015298477588>]
{<__main__.Person object at 0x00000152982D8DD8>}


'''
通过debug模式调试看到过程:针对set(p_lst)
1 集合是个去重的过程,判断一个元素是否是重复的看的是hash值
2 调用hash值就触发了__hash__,找到元素的hash值
    首先<__main__.Person object at 0x00000152982D8DD8>开始放入集合中,接着<__main__.Person object at 0x00000152982D8E10>也触发__hash__放入集合中
3 两个元素的比较接着触发了 __eq__ ,而eq设置的判断标准,它们几乎一样,返回true ,也就是这两个元素的hash判断标准而言,它们是相等的,这样的话,元素<__main__.Person object at 0x00000152982D8E10>被去除了
4 以此类推,接着下一个元素
5 最后只会剩下唯一的第一个元素
View Code
原文地址:https://www.cnblogs.com/mmyy-blog/p/9237306.html