No.010-Python-学习之路-Day7-面向对象的进阶<类中高级方法|类创建及实例的生成过程|反射>|异常处理|动态的模块导入|断言

类中高级方法

静态方法<装饰器@staticmethod>

  • 静态方法仅仅名义上归类管理,但实际上静态方法未利用任何的类中属性;
class Dog(object):
    def __init__(self, name):
        self.name = name
    @staticmethod # 静态方法->实际上跟类没什么关系,仅仅是一个方法,仅仅是需要使用类名来调用
    def eat(food):
        # 因为没有传self进去,则无法使用self.name之类的内容调用
        # 目的就是不在函数内使用
        # 相当于类的工具包
        print("Unknow dog is eating %s" %food)
        
d = Dog("XiLuo")
d.eat("Shit")

类方法<装饰器@classmethod>

  • 类方法仅仅可以访问类变量,无法访问实例变量;
class Market(object):
    discount = 10.0
    goods = {"macbook": {"sn": "No.001","price": 9600,"counts": 100},
            "iphone": {"sn": "No.002","price": 5600,"counts": 50}
    }

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

    @classmethod
    def set_discount(cls, new_discount):
        print("Discount change from %.1f to %.1f" %(cls.discount, new_discount))
        cls.discount = new_discount

    def get_price(self):
        for good in self.goods:
            print("商品名-{} 编号-{} 价格-{}".format(
                good,
                self.goods[good]['sn'],
                self.goods[good]['price'] * self.discount
            ))


mymarket = Market("小苹果百货店", "Amadeus Lee", "尚阳城")

mymarket.get_price() 
mymarket.set_discount(9.0) # 可以更改类变量discount的值
mymarket.get_price() 
Market.set_discount(8.0) # 可以更改类变量discount的值
Market.get_price(mymarket)
@classmethod

属性方法<装饰器@property>

  •  @ property 是把一个方法变成一个静态属性
# @ property 是把一个方法变成一个静态属性

# 属性方法如何赋值?
#   使用类似于width.setter的方法来来赋值
# 属性方法如何删除?
#   使用类似于width.deleter的方法来删除
# 把一个方法变成静态属性的作用:
#   1.将直接暴露的属性隐藏,只有通过一定的限定才能够赋值或者取值,而且仍然以属性的方式调用,而非函数
#   2.有些属性是动态的,比如航班的实时状态,如果需要取得这个值,需要执行<联系API->调用函数分析->返回结果>
#     ,这些使用属性方法直接生成,在类外看起来就是一个属性<隐藏实现细节>,而非相对复杂的函数操作;

class Screen(object):
    '''
    显示屏类,定义长和宽
    '''
    def __init__(self):
        self.__width = None
        self.__height = None

    @property # 将width变为属性,目的为了限制width相关的信息,希望其设置的值在范围内
    def width(self):
        return self.__width

    @width.setter # 可以调用方法进行修改
    def width(self, width):
        if isinstance(width, int):
            if 1366 >= width >= 1024:
                self.__width = width
            else:
                raise ValueError("width范围为1024-1366")
        else:
            raise ValueError("width不是int类型")

    @property # 将height变为属性
    def height(self):
        return self.__height

    @height.setter # 可以调用方法进行修改
    def height(self, height):
        if isinstance(height, int):
            if 768 >= height >= 256:
                self.__height = height
            else:
                raise ValueError("height的范围为256-768")
        else:
            raise ValueError("height不是int类型")

    @height.deleter # 当使用del xxx 的时候显示的信息;
    def height(self):
        self.__height = None
        print("已删除height!!!")

    @property # 这个是一个只读属性,没有定义赋值的方法哦;
    def resolution(self):
        if isinstance(self.__width, int) and isinstance(self.__height, int):
            if self.__width * self.__height == 786432:
                return "测试通过"
            else:
                return "测试失败"
        else:
            raise ValueError("width及height必须为int类型")


s = Screen()
s.width = 1024
s.height = 768
print(s.width, s.height, s.resolution)
@property定义的可操作属性及只读属性

类中的特殊方法

  • __init__(self) : obj init method;
  • __call__(self, *args, **kwargs) :Called when the instance is “called” as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
  • __str__(self):compute the “informal” or nicely printable string representation of an object. The return value must be a string object.When a object is Called by str(object) and the built-in functions format() and print();
  • obj/class.__doc__: return the description of the class or class of the obj;
  • obj/class.__module__: the class of obj is import from which module , return the module's name;
  • obj.__class__: return the class of the obj;
  • obj/class.__dict__ : return all attribute of a class or a obj , class only include class attrubute, not include instance attribute;obj have them all;
class Dog(object):
    '''
        这个类是描述狗这个对象的
    '''
    type = "dog"
    def __init__(self):
        self.name = "Baby"

    def get_name(self):
        print("come from get_name fun", self.name)

    
    def __call__(self, *args, **kwargs):
        self.name = args[0]
        self.type = args[1]

    def __str__(self):
        return "<obj>:%s" % self.name # 可以使用打印的时候看到比如下面的print(d) # 在jango中使用较多

d = Dog()

d("Bruce", "Mydog") # call方法的调用1 # 如果涉及类变量不会更改,只会覆盖
print("obj.__call__", d.name, d.type)
Dog()("Amadeus", "yourdog") #call方法的调用2 # 如果涉及实例及类变量不会更改不会覆盖
print("class.__call__", Dog.name, Dog.type)

print(d) # 显示显示信息-->调用的是__str__(self)方法

print(Dog.__doc__) # 打印这个类的描述信息
print(d.__doc__) # 打印这个类的描述信息

print("__module__",Dog.__module__, d.__module__) # 表示当前操作的对象的模块,即是从哪个模块导入的
print("__module__",d.__class__) # 表示当前的对象属于什么类


print(Dog.__dict__) #类调用打印类里的所有属性,不包括实例属性
print(d.__dict__) # 实例调用,打印实例中的属性
d.age = 2
print(d.__dict__) # 实例调用,打印实例中的属性
类特殊方法实例、
  • __getitem__(self, key): # 在使用obj[xxx]取值时即调用这个方法
  • __setitem__(self, key, value): # 在使用obj[key] = value赋值使,使用这个方法
  • __delitem__(self, key): # 在使用del obj[key] 时调用这个函数,具体操作自定义
class Foo(object):  # 自封装字典 # 2.7里面可以变成列表,3.0里面没有

    def __init__(self):
        self.dic = {}

    def __getitem__(self, key): # 在使用obj[xxx]取值时即调用这个方法
        print('__getitem__', key)
        return self.dic[key]

    def __setitem__(self, key, value): # 在使用obj[key] = value赋值使,使用这个方法
        print('__setitem__', key, value)
        self.dic[key] = value

    def __delitem__(self, key): # 在使用del obj[key] 时调用这个函数,具体操作自定义
        print('__delitem__', key)
        self.dic.pop(key)

obj = Foo()
obj['name'] = "Bruce"
obj['age'] = 19
del obj['name']
print(obj)
try:
    print(obj['name'])
except KeyError as KeyNotFound:
    print("key-name已经被删除!")
print(obj['age'])
自封装字典类型

 类的创建及实例的生成

class Foo(object):  # 定义一个类
    def __init__(self, name):
        self.name = name
f = Foo("Bruce") # 实例化这个类

以上代码中,f是对象, Foo是类;在python中一切皆对象,那么Foo这个类本身也是一个对象,是对象就有相应的类,那么它的类是什么呢?

>>> type(f)
<class '__main__.Foo'>  # 可以看到对象f的class是 Foo
>>> type(Foo)    
<class 'type'> # 可以看到对象Foo的class是type

以上代码中,类Foo身为对象时的类是type;也就是说我们可以直接通过实例化一个type类来创建Foo,如下:

# 定义一个单独的函数
def func(self):
    print('hello Bruce!!')
# 定义一个单独的函数
def init(self, name, age):
    self.name = name
    self.age = age

# 使用以上参数生成一个type类的对象Foo
Foo = type('Fooo', (object,), {'talk':func, '__init__':init}) 
# 可以看到如正常的Foo类一样
print(type(Foo))
f = Foo('Bruce', 12)
print(f.age)

 So...从上面所有的来看,obj<f>的class是Foo类,Foo类的类是type.......type呢则是python解释器自己来创建的;

事实上这也是我们在使用class关键字的时候Python所做的事情。Python通过使用metaclass-<这里是metaclass = type>来实现这一过程。

 

一个类的创建过程详解

__metaclass__ :

  • 源类,指定源类的参数,是用来生成类的类,比如type即是源类;
  •  影响class初始化过程、修改class的内容,返回修改过的class
  • 一般用来做一些逻辑比较复杂的操作,比如自省,修改继承,以及改变类的默认attribute比如__dict__等;

__new__(cls, *args, **kwargs):

  • 用来创建一个类的实例的,起创建一个类实例的作用构造器(constructor);
  • new第一个参数为cls,因为new工作时self并不存在,所以要传入一个类<cls>用于创建对象<self>;

__init__(self, *args, **kwargs):

  • 用来初始化一个实例的,起初始化一个已被创建的实例的作用, 初始化(initializer);

class Mytype(type):

    def __init__(self, what, bases=None, dict=None):
        super(Mytype, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        print('--Mytype call--')
        obj = self.__new__(self, *args, **kwargs)
        self.__init__(obj, *args, **kwargs)
     return obj

class Foo(object, metaclass=Mytype):  # 源类,即类的类,可以重写, 在python3中需要写在class内部

    __metaclass__ = Mytype

    def __new__(cls, *args, **kwargs):
        print("Foo __new__")
        self = object.__new__(cls)  # 建立对象,返回对象的内存地址# 继承父类的__new__()方法,不继承无法实例化
        print(self)
        return self

    def __init__(self):
        self.name = "Bruce"


f = Foo() 
# Foo()中的“()”是Foo作为实例call其类<源类>的__call_方法,即本质是呼叫源类 Mytype中的__call__方法;
# 而在__call__方法中,传入了Mytype的对象即<self = Foo>,并以此调用Foo.__new__及Foo.__init__方法来创建Foo类的对象;
# __new__方法通过使用父类中的__new__方法成功建立了一个对象<开辟了内存空间>并将其返回, 则 obj = __new__.return即新生成的对象<空内存区域>;
# 随后调用__init__方法,传入obj<__new__生成的对象的指针>及相关变量值,完成类实例的初始化,并将obj返回,则f = obj,完成整个Foo的实例的创建;
# 详解在这里面
'''https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python'''

 再具体点如下:

class MyType(type):
    def __init__(self,*args,**kwargs):

        print("Mytype __init__",*args,**kwargs)
        super(MyType, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("Mytype __call__", *args, **kwargs)
        obj = self.__new__(self)
        print("obj ",obj,*args, **kwargs)
        print(self)
        self.__init__(obj,*args, **kwargs)
        return obj

    def __new__(cls, *args, **kwargs):
        print("Mytype __new__",*args,**kwargs)
        return type.__new__(cls, *args, **kwargs)


def __new__(cls, *args, **kwargs):
    print("Foo __new__",cls, *args, **kwargs)
    return object.__new__(cls)
def __init__(self,name):
    self.name = name

# 生成MyType的实例Foo,则会先调用MyType类的__new__,然后再调用__init__完成Foo的创建
Foo = MyType('Foo', (object, ), {"__new__": __new__, "__init__": __init__})
# 生成Foo的实例f,过程如上
f = Foo("Bruce")
print("f",f)
print("fname",f.name)
完整的实例创建过程

Metaclass可以不是一个类,也可以是一个函数,如下:

def myMeta(class_name , bases, class_attributes):
    print("class_name-{}\nbases-{}\nclass_attributes-{}".format(
        class_name, bases, class_attributes
    ))
    return type(class_name, bases, class_attributes)
# 从这里可以看出定义类的关键字class,其真实操作即:
# Foo = metaclass(class_name="Foo", bases=(object,), class_attributes={})
class Foo(object, metaclass=myMeta):

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

    def get_name(self):
        print(self.name)
f = Foo("Bruce")
f.get_name()
一个函数作为metaclass

metaclass的一个应用,我希望所有自定义类的属性名字都是大写,可以使用metaclass来实现:

class MyMeta(type):

    def __new__(cls, class_name, base, attributes):
        upper_attributes = {}

        for key , var in attributes.items():
            if key.startswith("__"):
                upper_attributes[key] = var
            else:
                upper_attributes[key.upper()] = var
        print(upper_attributes)
        return super(MyMeta, cls).__new__(cls, class_name, base, upper_attributes)

class Foo(object, metaclass=MyMeta):
    def __init__(self):
        self.name = "Bruce"
    def get_name(self):
        print(self.name)
f = Foo()
print(hasattr(f, "GET_NAME"))
print(hasattr(f, "get_name"))
metaclass使新建类属性名大写

反射

反射->通过字符串映射或修改程序运行时的状态、属性、方法,有以下4个方法:

hasattr(object, name):

  • 判断object对象中是否存在name属性
  • getattr(object, name[, default]):
  • 获取object对象的属性的值,如果存在则返回属性值,如果不存在分为两种情况,一种是没有default参数时,会直接报错;
  • 给定了default参数,若对象本身没有name属性,则会返回给定的default值;

getattr(object, name[, default])

  • 返回object的名字为str{name}的attribute方法;
  • 如果没有这个属性,返回default的值<如果设置>,否则报错;

setattr(object, name, value)

  • 给object对象的name属性赋值value,如果对象原本存在给定的属性name,则setattr会更改属性的值为给定的value;
  • 如果对象原本不存在属性name,setattr会在对象中创建属性,并赋值为给定的value;

delattr(object, name)

  • 用来删除指定对象的指定名称的属性,和setattr函数作用相反;

callable(object)

  • 函数用于检查一个对象是否是可调用的;
  • 返回 True,object 仍然可能调用失败;但如果返回 False,调用对象 object 绝对不会成功;
  • 对于函数、方法、lambda 函式、 类以及实现了 __call__ 方法的类实例, 它都返回 True;
def bulk(self):
    print("%s is yelling..." )
class Dog(object):
    def __init__(self, name):
        self.name = name
    def eat(self):
        print("%s is eating ..." % self.name)
d = Dog("Vick")
choice = input(">>:").strip() # 用户通过输入调用instance的方法;
if hasattr(d, choice): # 判断在某个instance中是否有这个字符串对应的方法 # 如果是属性呢,属性无法执行,如何而从新赋值呢?setattr
    print(getattr(d, choice)) # 映射出了eat函数对的内存对象地址
    getattr(d,choice)() # 然后执行
else:
    setattr(d, choice, bulk) # 动态装入一个方法,也可以动态装入一个属性
    getattr(d, choice)(d) # 因为没在类中 self没有自动传入
delattr(d, "eat") # 相当于 del d.eat 删除object中的某一属性

 异常处理

常用的异常之类,后期补充

  • AttributeError 试图访问一个对象没有的树形,比如foo.x 但是foo没有x
  • IoError 输入/输出一场,基本上是无法打开文件
  • ImportError 无法引入模块或包;基本上是路径问题或名称错误
  • IndentationError 语法错误(的子类);代码缩进有问题
  • IndexError index超出界限
  • KeyError 试图访问字典里不存在的键
  • KeyboardInterrupt Ctrl+C 被按下
  • NameError 使用一个还未被赋予对象的变量
  • SyntaxError 代码不能编译,一般是语法错误
  • TypeError 传入对象类型与要求的不符合
  • UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于令有一个同名的全局变量,导致你以为正在访问它
  • ValueError 传入一个调用者不期望的值,即使值的类型是正确的

 异常抓取具体语法:

try:
    #data['name']
    #name[3]
    open('test.txt')
    a = 1
    print(a)
except (KeyError, IndexError) as ke: # 一次判断多个错误,但是不知道是哪个代码出错了,一般不这么写
    print("Index或者key不存在", ke)          # 可以用在不管出来什么错误,处理方法都是相同的
except Exception as e:  # 可以抓住所有错误,都可以抓住 # 前面写明细,后面抓所有,防止出现前面未抓到的某些错误
                        # 注意IndentationError SyntaxError 这种是无法抓的,因为根本编译不了
    print("出错了", e)
else: # 没有出现错误的时候执行这个
    print("一切正常")
finally: # 不管有没有错误都会执行
    print("不管有没有错误,都执行")

自定义异常并触发

class BruceException(Exception):
    def __init__(self, msg):
        self.message = msg
    def __str__(self): # 父类中存在这个方法,即return self.message
         return super(BruceException, self).__str__()
try:
    raise BruceException('数据库连不上') # 使用raise直接触发自己写的异常
except BruceException as e:
    print(e)
    
# 题外话#####__str__的作用,即在str(obj)的时候生效###
fault = BruceException("My fault!!") # 实例化
print(str(fault))  # 调用obj的__str__方法  ##,当然不止str()可以触发,只要将obj-->str即会触发;

 动态的模块导入

正常导入操作如下:

import lib.aa 

如果我有以下的需求如何实现呢?

# 已知
modname = "aa"
# 需要导入lib.aa,尝试:
from lib import modname #这样是无效的 类似于from lib import "aa"

以上需求有两种方式,第一种方式为python解释内部机制使用,如下:

modname = "lib.aa"
lib = __import__(modname) # 只导入lib的级别
print(lib)
lib.aa.myfun()

另外一种为正常代码编写过程中使用,如下:

import importlib
modname = "lib.aa"
mod = importlib.import_module(modname) # 写到哪层就是哪层
print(mod)
mod.myfun()

 断言

name = "Bruce"
assert type(name) is str # 断言正确接着执行
print("aaa")
assert type(name) is int # 断言错误报错
print("bbb")
# 断言的作用在于,之后的程序依赖于前面的断言通过,而程序相对重要,不能出错;

 

 

 

 

 

 

 

 

 

 

 

 

end

posted @ 2020-02-17 15:13  FcBlogs  阅读(89)  评论(0编辑  收藏  举报