python3 定制类

定制类

  一个类可以通过定义具有特殊名称的方法来实现由特殊语法所引发的特定操作。

  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 "<stdin>", line 1, in <module>
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": "1489088761@qq.com",
        "password": "A1234568","remember": 0
    }
    HttpRequest(url,data).send_post(url,data)

 

绑定方法 

特殊之处在于将调用者本身当做第一个参数自动传入。

1.绑定给对象的方法:调用者是对象,自动传入的是对象。

2.绑定给类的方法:调用者是类,自动传入的是类。@classmethod

@classmethod

def a(cls):

  pass

 

非绑定方法

静态方法,没有绑定给任何人,调用者是对象,类。不会传入调用者。

@staticmethod将下属函数装饰成静态方法

不写@staticmethod来装饰,比如

class a:

  def b():

    pass

方法b只能被类来调用,不能被对象来调用。

 还可以看看https://www.cnblogs.com/rainbow-ran/p/12204913.html

 

 

 

详细的可以参考: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,不一一列举了,纯做记录。

posted @ 2020-04-03 15:34  云long  阅读(236)  评论(0编辑  收藏  举报