定制类
一个类可以通过定义具有特殊名称的方法来实现由特殊语法所引发的特定操作。
python的特殊方法:
-
特殊方法定义在class中
-
不需要直接调用
-
Python的某些函数或者操作符会调用对应的特殊方法
正确实现特殊方法:
- 只需要编写用到的特殊方法
- 有关联性的特殊方法必须实现
class Custom(object): __slots__ = ('_name','index') def __init__(self,name): self._name = name self.index = -1 def __str__(self): return 'Custom Instance => (name: %s)' % self._name __repr__ = __str__ def __len__(self): return len(self._name) def __iter__(self): return self def next(self): self.index+=1 if self.index >= len(self._name): raise StopIteration(); return self._name[self.index] def __getitem__(self,n): return self._name[n] def __getattr__(self,attr): if attr=='func': return lambda: 'FFF' elif attr=='attr': return 'attr' raise AttributeError('\'Custom\' object has no attribute \'%s\'' % attr) def __call__(self): print "Instance's name is %s." % self._name
1.__slots__的用法
Python是动态语言,任何实例在运行期都可以动态地添加属性。
__slots__变量,用来限制该class实例能添加的属性。
没有被放到__slots__中的属性将不能被绑定,试图绑定将得到AttributeError的错误。
__slots__
定义的属性仅对当前类实例起作用,对父类和子类没有影响。
在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
>>> s = Custom() # 创建新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.index = 25 # 绑定属性'index' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "", line 1, in AttributeError: 'Custom' object has no attribute 'score'
2. __str__和__repr__的用法
>>> class Student(object): ... def __init__(self, name): ... self.name = name ... >>> print(Student('Michael')) <__main__.Student object at 0x109afb190>
不是很好看,返回一个好看的字符串就要用到__str__()方法。
>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Michael')) Student object (name: Michael)
必须配合print()才可以,若是不用print(),如下:
>>> s = Student('Michael') >>> s <__main__.Student object at 0x109afb310>
直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
解决办法是再定义一个__repr__()
。但是通常__str__()
和__repr__()
代码都是一样的,加上: __repr__ = __str__
3. __iter__和__next__的用法
一个类想被用于for ... in
循环,类似list或tuple那样,必须实现__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 for n in Fib(): print(n)
4.__getitem__和__setitem__()的用法
通过__getitem__方法,可以表现得像list那样按照下标取出元素。self[key],key为int, slice。
class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L f = Fib() print(f[0:5]) print(f[5])
通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
只要实现了__getitem__
和 __len__
方法,就会被认为是序列。
定制的容器是可变的,那么还需要定义 __setitem__ 和__delitem__方法。
class Tag: def __init__(self): self.change={'python':'This is python', 'php':'PHP is a good language'} def __getitem__(self, item): print('调用getitem') return self.change[item] def __setitem__(self, key, value): print('调用setitem') self.change[key]=value def __delitem__(self, key): print("delitem") del self.change[key] a=Tag() print(a['php']) a['php']='PHP is not a good language' print(a['php']) del a["php"]
5. __getattr__
正常情况下,当我们调用类的方法或属性时,如果实例和类中都不存在,就会报错。__getattr__()
方法,动态返回一个属性:
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
当调用不存在的属性时,比如score
,Python解释器会试图调用__getattr__(self, 'score')
来尝试获得属性,这样,我们就有机会返回score
的值:
>>> s = Student() >>> s.name 'Michael' >>> s.score 99
注意,只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name
,不会在__getattr__
中查找。
返回函数也是可以的。
__getattr__
默认返回就是None
。我们就要按照约定,抛出AttributeError
的错误:
class Student(object): def __getattr__(self, attr): if attr=='age': return 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
__setattr__(), __delattr__()方法用法用到再搜索吧。
6. __call__和callable()的用法
一般情况下,对象无法被调用,函数可以被调用。一个实例变成可调用对象,只需要实现一个特殊方法__call__()
。
Python中一切都可以看做对象,函数也是对象。通过__call__,也可以反过来,把对象看成是函数。对象后加上(),就执行一个方法,即__call__方法。__call__()
还可以定义参数。
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
>>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael.
使用callable()
函数可以判断某个变量是对象还是函数,返回bool值。能被调用的对象就是一个Callable
对象。
7. 别的特殊方法
别的特殊方法,比如__cmp__,
__len__,
__new__等用到再查。@property之前的笔记中有。
链式调用
在python中实现链式调用只需在函数返回对象自己就行了
class Person: def name(self, name): self.name = name return self def age(self, age): self.age = age return self def show(self): print "My name is", self.name, "and I am", self.age, "years old." p = Person() p.name("Li Lei").age(15).show()
class Chain(object): def __init__(self, path=''): self.__path = path def __getattr__(self, path): return Chain('%s/%s' % (self.__path, path)) def __call__(self, path): return Chain('%s/%s' % (self.__path, path)) def __str__(self): return self.__path __repr__ = __str__ print(Chain().users('michael').repos) # /users/michael/repos
评论中的解释,感觉好,引用下。
Step 1:
Chain() # 实例化
Step 2:
Chain().users
# 由于没有给实例传入初始化对应属性的具体信息,从而自动调用__getattr__()函数,从而有:
Chain().users = Chain('\users') # 这是重建实例
Step 3:
Chain().users('michael')
Chain().users('michael') = Chain('\users')('michael') # 这是对实例直接调用,相当于调用普通函数一样
# 关键就在这步,上面的朋友没有说明晰(并不是说你们不懂),这一步返回的是Chain('\users\michael'),再一次重建实例,覆盖掉Chain('\users'),
#记 renew = Chain('\users\michael'), 此时新实例的属性renew.__path = \users\michael;
Step 4:
Chain().users('michael').repos
# 这一步是查询renew实例的属性repos,由于没有这一属性,就会执行__getattr__()函数,再一次返回新的实例Chain('\users\michael\repos')并且覆盖点之前的实例,
# 这里记 trinew =Chain('\users\michael\repos'),不要忘了,一单定义了一个新的实例,就会执行__init__方法;
Step 5:
print(Chain().users('michael').repos) = print(trinew) #由于我们定义了__str__()方法,那么打印的时候就会调用此方法,据此方法的定义,打印回来的是trinew的__path属#性,即——\users\michael\repos 。至此,我们也把所有定义的有特殊用的方法都用上了,完毕。
实例方法、类方法、静态方法
实例方法
定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
调用:只能由实例对象调用。
类方法
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:实例对象和类对象都可以调用。
静态方法
定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
调用:实例对象和类对象都可以调用。
import requests class HttpRequest(): def __init__(self,url,data): self.url = url self.data = data # todo 实例方法 def send_post(self,url,data): # todo 实例方法,只能通过实例来调用 res = requests.post(url,data) print(res.status_code) @classmethod def add(cls,x,y): print('我是类方法') return x+y @staticmethod def print_msg(): print('我是静态方法') if __name__ == '__main__': # todo 类方法和静态方法可以直接类名.方法名直接调用,可以绕过实例方法的初始化函数 print(HttpRequest.add(7,8)) HttpRequest.print_msg() url = 'https://www.ketangpai.com/UserApi/login' data = { "email": "[email protected]", "password": "A1234568","remember": 0 } HttpRequest(url,data).send_post(url,data)
详细的可以参考:https://blog.csdn.net/w469001293/article/details/104227014?depth_1-utm_source=distribute.pc_relevant_right.none-task&utm_source=distribute.pc_relevant_right.none-task
参考很多网友的帖子和廖老师的pyhton3,不一一列举了,纯做记录。