백엔드 개발 파이썬 튜토리얼 python中实现定制类的特殊方法总结

python中实现定制类的特殊方法总结

Jun 06, 2016 am 11:32 AM
python

看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。

__slots__我们已经知道怎么用了,__len__()方法我们也知道是为了能让class作用于len()函数。

除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。

__str__

我们先定义一个Student类,打印一个实例:

代码如下:


>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...
>>> print Student('Michael')
<__main__.student object at>


打印出一堆<__main__.student object at>,不好看。

怎么才能打印得好看呢?只需要定义好__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,打印出来的实例还是不好看:

代码如下:


>>> s = Student('Michael')
>>> s
<__main__.student object at>


这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:

代码如下:


class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

__iter__

如果一个类想被用于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 # 返回下一个值

现在,试试把Fib实例作用于for循环:

代码如下:


>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
75025

__getitem__

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

代码如下:


>>> Fib()[5]
Traceback (most recent call last):
  File "", line 1, in
TypeError: 'Fib' object does not support indexing


要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

代码如下:


class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a


现在,就可以按下标访问数列的任意一项了:

代码如下:


>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101


但是list有个神奇的切片方法:

代码如下:


>>> range(100)[5:10]
[5, 6, 7, 8, 9]


对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

代码如下:


class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice):
            start = n.start
            stop = n.stop
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L


现在试试Fib的切片:

代码如下:


>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]


但是没有对step参数作处理:

代码如下:


>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]


也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

__getattr__

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类:

代码如下:


class Student(object):

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


调用name属性,没问题,但是,调用不存在的score属性,就有问题了:

代码如下:


>>> s = Student()
>>> print s.name
Michael
>>> print s.score
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'score'


错误信息很清楚地告诉我们,没有找到score这个attribute。

要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__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


返回函数也是完全可以的:

代码如下:


class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25


只是调用方式要变为:

代码如下:


>>> s.age()
25


注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

代码如下:


class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)


这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

举个例子:

现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

http://api.server/user/friends
http://api.server/user/timeline/list

如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

利用完全动态的__getattr__,我们可以写出一个链式调用:

代码如下:


class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path


试试:

代码如下:


>>> Chain().status.user.timeline.list
'/status/user/timeline/list'


这样,无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

还有些REST API会把参数放到URL中,比如GitHub的API:

代码如下:


GET /users/:user/repos


调用时,需要把:user替换为实际用户名。如果我们能写出这样的链式调用:

代码如下:


Chain().users('michael').repos


就可以非常方便地调用API了。有兴趣的童鞋可以试试写出来。

__call__

一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?类似instance()?在Python中,答案是肯定的。

任何类,只需要定义一个__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()
My name is Michael.


__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call()__的类实例:

代码如下:


>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('string')
False


通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

小结

Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

XML 형식을 아름답게하는 방법 XML 형식을 아름답게하는 방법 Apr 02, 2025 pm 09:57 PM

XML 미화는 합리적인 압입, 라인 브레이크 및 태그 구성을 포함하여 기본적으로 가독성을 향상시키고 있습니다. 원칙은 XML 트리를 가로 지르고 레벨에 따라 들여 쓰기를 추가하고 텍스트가 포함 된 빈 태그와 태그를 처리하는 것입니다. Python의 xml.etree.elementtree 라이브러리는 위의 미화 프로세스를 구현할 수있는 편리한 Pretty_XML () 기능을 제공합니다.

XML 형식을 여는 방법 XML 형식을 여는 방법 Apr 02, 2025 pm 09:00 PM

대부분의 텍스트 편집기를 사용하여 XML 파일을여십시오. 보다 직관적 인 트리 디스플레이가 필요한 경우 Oxygen XML 편집기 또는 XMLSPy와 같은 XML 편집기를 사용할 수 있습니다. 프로그램에서 XML 데이터를 처리하는 경우 프로그래밍 언어 (예 : Python) 및 XML 라이브러 (예 : XML.etree.elementtree)를 사용하여 구문 분석해야합니다.

XML 수정에 프로그래밍이 필요합니까? XML 수정에 프로그래밍이 필요합니까? Apr 02, 2025 pm 06:51 PM

XML 컨텐츠를 수정하려면 프로그래밍이 필요합니다. 대상 노드를 추가, 삭제, 수정 및 확인하려면 정확한 찾기가 필요하기 때문입니다. 프로그래밍 언어에는 XML을 처리하기위한 해당 라이브러리가 있으며 운영 데이터베이스와 같이 안전하고 효율적이며 제어 가능한 작업을 수행 할 수있는 API를 제공합니다.

XML을 PDF로 변환 할 수있는 모바일 앱이 있습니까? XML을 PDF로 변환 할 수있는 모바일 앱이 있습니까? Apr 02, 2025 pm 08:54 PM

XML을 PDF로 직접 변환하는 응용 프로그램은 근본적으로 다른 두 형식이므로 찾을 수 없습니다. XML은 데이터를 저장하는 데 사용되는 반면 PDF는 문서를 표시하는 데 사용됩니다. 변환을 완료하려면 Python 및 ReportLab과 같은 프로그래밍 언어 및 라이브러리를 사용하여 XML 데이터를 구문 분석하고 PDF 문서를 생성 할 수 있습니다.

휴대 전화 용 무료 XML에서 PDF 도구가 있습니까? 휴대 전화 용 무료 XML에서 PDF 도구가 있습니까? Apr 02, 2025 pm 09:12 PM

모바일에는 간단하고 직접 무료 XML에서 PDF 툴이 없습니다. 필요한 데이터 시각화 프로세스에는 복잡한 데이터 이해 및 렌더링이 포함되며 시장에있는 소위 "무료"도구의 대부분은 경험이 좋지 않습니다. 컴퓨터 측 도구를 사용하거나 클라우드 서비스를 사용하거나보다 신뢰할 수있는 전환 효과를 얻기 위해 앱을 개발하는 것이 좋습니다.

휴대폰에서 XML을 PDF로 변환 할 때 변환 속도가 빠르나요? 휴대폰에서 XML을 PDF로 변환 할 때 변환 속도가 빠르나요? Apr 02, 2025 pm 10:09 PM

모바일 XML에서 PDF의 속도는 다음 요인에 따라 다릅니다. XML 구조의 복잡성. 모바일 하드웨어 구성 변환 방법 (라이브러리, 알고리즘) 코드 품질 최적화 방법 (효율적인 라이브러리 선택, 알고리즘 최적화, 캐시 데이터 및 다중 스레딩 사용). 전반적으로 절대적인 답변은 없으며 특정 상황에 따라 최적화해야합니다.

휴대 전화에서 XML을 PDF로 변환하는 방법은 무엇입니까? 휴대 전화에서 XML을 PDF로 변환하는 방법은 무엇입니까? Apr 02, 2025 pm 10:18 PM

휴대 전화에서 XML을 PDF로 직접 변환하는 것은 쉽지 않지만 클라우드 서비스를 통해 달성 할 수 있습니다. 가벼운 모바일 앱을 사용하여 XML 파일을 업로드하고 생성 된 PDF를 수신하고 클라우드 API로 변환하는 것이 좋습니다. Cloud API는 Serverless Computing Services를 사용하고 올바른 플랫폼을 선택하는 것이 중요합니다. XML 구문 분석 및 PDF 생성을 처리 할 때 복잡성, 오류 처리, 보안 및 최적화 전략을 고려해야합니다. 전체 프로세스에는 프론트 엔드 앱과 백엔드 API가 함께 작동해야하며 다양한 기술에 대한 이해가 필요합니다.

XML을 이미지로 변환하는 프로세스는 무엇입니까? XML을 이미지로 변환하는 프로세스는 무엇입니까? Apr 02, 2025 pm 08:24 PM

XML 이미지를 먼저 변환하려면 먼저 XML 데이터 구조를 결정한 다음 Python의 Matplotlib와 같은 적절한 그래픽 라이브러리를 선택하고 데이터 구조를 기반으로 시각화 전략을 선택하고 데이터 볼륨 및 이미지 형식을 고려하고 효율적인 라이브러리를 수행하거나 필요에 따라 PNG, JPEG 또는 SVG로 저장하십시오.

See all articles