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)
属性方法<装饰器@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)
类中的特殊方法
- __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 forx.__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来实现:
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"))
反射
反射->通过字符串映射或修改程序运行时的状态、属性、方法,有以下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