Python - 进阶篇(下)

简介: Python - 进阶篇(下)

五、面向对象编程基础

1.简介

image.png

image.pngimage.png

2.Python之定义类并创建实例

在Python中,类通过 class 关键字定义。以 Person 为例,定义一个Person如下:

classPerson(object):
pass

按照 Python 的编程习惯,类名以大写字母开头,紧接着是(object),表示该类是从哪个类继承下来的。类的继承将在后面的章节讲解,现在我们只需要简单地从object类继承。

有了Person类的定义,就可以创建出具体的xiaoming、xiaohong等实例。创建实例使用 类名+(),类似函数调用的形式创建:

xiaoming=Person()
xiaohong=Person()

3.Python中创建实例属性

虽然可以通过Person类创建出xiaoming、xiaohong等实例,但是这些实例看上除了地址不同外,没有什么其他不同。在现实世界中,区分xiaoming、xiaohong要依靠他们各自的名字、性别、生日等属性。

如何让每个实例拥有各自不同的属性?由于Python是动态语言,对每一个实例,都可以直接给他们的属性赋值,例如,给xiaoming这个实例加上name、genderbirth属性:

xiaoming=Person()
xiaoming.name='Xiao Ming'xiaoming.gender='Male'xiaoming.birth='1990-1-1'

xiaohong加上的属性不一定要和xiaoming相同:

xiaohong=Person()
xiaohong.name='Xiao Hong'xiaohong.school='No. 1 High School'xiaohong.grade=2

实例的属性可以像普通变量一样进行操作:

xiaohong.grade=xiaohong.grade+1

任务

请创建包含两个 Person 类的实例的 list,并给两个实例的 name 赋值,然后按照 name 进行排序。

classPerson(object):
passp1=Person()
p1.name='Bart'p2=Person()
p2.name='Adam'p3=Person()
p3.name='Lisa'L1= [p1, p2, p3]
L2=sorted(L1,key=lambdax: x.name)
printL2[0].nameprintL2[1].nameprintL2[2].name

4.Python中初始化实例属性

虽然我们可以自由地给一个实例绑定各种属性,但是,现实世界中,一种类型的实例应该拥有相同名字的属性。例如,Person应该在创建的时候就拥有 name、gender birth 属性,怎么办?

在定义 Person 类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,__init__()方法被自动调用,我们就能在此为每个实例都统一加上以下属性:

classPerson(object):
def__init__(self, name, gender, birth):
self.name=nameself.gender=genderself.birth=birth

__init__() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。

相应地,创建实例时,就必须要提供除 self 以外的参数:

xiaoming=Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong=Person('Xiao Hong', 'Female', '1992-2-2')

有了__init__()方法,每个Person实例在创建时,都会有 name、gender birth 这3个属性,并且,被赋予不同的属性值,访问属性使用.操作符:

printxiaoming.name# 输出 'Xiao Ming'printxiaohong.birth# 输出 '1992-2-2'

要特别注意的是,初学者定义__init__()方法常常忘记了 self 参数:

>>>classPerson(object):
...     def__init__(name, gender, birth):
...         pass... 
>>>xiaoming=Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (mostrecentcalllast):
File"<stdin>", line1, in<module>TypeError: __init__() takesexactly3arguments (4given)

这会导致创建失败或运行不正常,因为第一个参数name被Python解释器传入了实例的引用,从而导致整个方法的调用参数位置全部没有对上。

5.Python中访问限制

我们可以给一个实例绑定很多属性,如果有些属性不希望被外部访问到怎么办?

Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问。看例子:

classPerson(object):
def__init__(self, name):
self.name=nameself._title='Mr'self.__job='Student'p=Person('Bob')
printp.name# => Bobprintp._title# => Mrprintp.__job# => ErrorTraceback (mostrecentcalllast):
File"<stdin>", line1, in<module>AttributeError: 'Person'objecthasnoattribute'__job'

可见,只有以双下划线开头的"__job"不能直接被外部访问。

但是,如果一个属性以"__xxx__"的形式定义,那它又可以被外部访问了,以"__xxx__"定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用"__xxx__"定义。


单下划线开头的属性"_xxx"虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。

6.Python中创建类属性

类是模板,而实例则是根据类创建的对象。

绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

定义类属性可以直接在 class 中定义:

classPerson(object):
address='Earth'def__init__(self, name):
self.name=name

因为类属性是直接绑定在类上的,所以,访问类属性不需要创建实例,就可以直接访问:

printPerson.address# => Earth

对一个实例调用类的属性也是可以访问的,所有实例都可以访问到它所属的类的属性:

p1=Person('Bob')
p2=Person('Alice')
printp1.address# => Earthprintp2.address# => Earth

由于Python是动态语言,类属性也是可以动态添加和修改的:

Person.address='China'printp1.address# => 'China'printp2.address# => 'China'

因为类属性只有一份,所以,当Person类的address改变时,所有实例访问到的类属性都改变了。

任务

请给 Person 类添加一个类属性 count,每创建一个实例,count 属性就加 1,这样就可以统计出一共创建了多少个 Person 的实例。

classPerson(object):
count=0def__init__(self, name):
self.name=namePerson.count=1+Person.countp1=Person('Bob')
printPerson.countp2=Person('Alice')
printPerson.countp3=Person('Tim')
printPerson.count

7.Python中类属性和实例属性名字冲突怎么办

修改类属性会导致所有实例访问到的类属性全部都受影响,但是,如果在实例变量上修改类属性会发生什么问题呢?

classPerson(object):
address='Earth'def__init__(self, name):
self.name=namep1=Person('Bob')
p2=Person('Alice')
print'Person.address = '+Person.addressp1.address='China'print'p1.address = '+p1.addressprint'Person.address = '+Person.addressprint'p2.address = '+p2.address

结果如下:

Person.address=Earthp1.address=ChinaPerson.address=Earthp2.address=Earth

我们发现,在设置了 p1.address = 'China' 后,p1访问 address 确实变成了 'China',但是,Person.address和p2.address仍然是'Earch',怎么回事?

原因是 p1.address = 'China'并没有改变 Person 的 address,而是给 p1这个实例绑定了实例属性address ,对p1来说,它有一个实例属性address(值是'China'),而它所属的类Person也有一个类属性address,所以:

访问 p1.address 时,优先查找实例属性,返回'China'。

访问 p2.address 时,p2没有实例属性address,但是有类属性address,因此返回'Earth'。

可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。

当我们把 p1 的 address 实例属性删除后,访问 p1.address 就又返回类属性的值 'Earth'了:

delp1.addressprintp1.address# => Earth

可见,千万不要在实例上修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个实例属性。

8.Python中定义实例方法

一个实例的私有属性就是以__开头的属性,无法被外部访问,那这些属性定义有什么用?

虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。除了可以定义实例的属性外,还可以定义实例的方法。

实例的方法就是在类中定义的函数,它的第一个参数永远是 self,指向调用该方法的实例本身,其他参数和一个普通函数是完全一样的:

classPerson(object):
def__init__(self, name):
self.__name=namedefget_name(self):
returnself.__name

get_name(self) 就是一个实例方法,它的第一个参数是self。__init__(self, name)其实也可看做是一个特殊的实例方法。

调用实例方法必须在实例上调用:

p1=Person('Bob')
printp1.get_name()  #self不需要显式传入# => Bob

在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。

9.Python中方法也是属性

我们在 class 中定义的实例方法其实也是属性,它实际上是一个函数对象:

classPerson(object):
def__init__(self, name, score):
self.name=nameself.score=scoredefget_grade(self):
return'A'p1=Person('Bob', 90)
printp1.get_grade# => <bound method Person.get_grade of <__main__.Person object at 0x109e58510>>printp1.get_grade()
# => A

也就是说,p1.get_grade 返回的是一个函数对象,但这个函数是一个绑定到实例的函数,p1.get_grade() 才是方法调用。

因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用 types.MethodType() 把一个函数变为一个方法:

importtypesdeffn_get_grade(self):
ifself.score>=80:
return'A'ifself.score>=60:
return'B'return'C'classPerson(object):
def__init__(self, name, score):
self.name=nameself.score=scorep1=Person('Bob', 90)
p1.get_grade=types.MethodType(fn_get_grade, p1, Person)
printp1.get_grade()
# => Ap2=Person('Alice', 65)
printp2.get_grade()
# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'# 因为p2实例并没有绑定get_grade

给一个实例动态添加方法并不常见,直接在class中定义要更直观。

10.Python中定义类方法

和属性类似,方法也分实例方法类方法

class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。

要在class中定义类方法,需要这么写:

classPerson(object):
count=0@classmethoddefhow_many(cls):
returncls.countdef__init__(self, name):
self.name=namePerson.count=Person.count+1printPerson.how_many()
p1=Person('Bob')
printPerson.how_many()

通过标记一个 @classmethod,该方法将绑定到 Person 类上,而非类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.count 实际上相当于 Person.count

因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。

11.待更新...

六、类的继承

1.简介

image.pngimage.pngimage.pngimage.pngimage.pngimage.png

image.png2.Python中继承一个类

如果已经定义了Person类,需要定义新的StudentTeacher类时,可以直接从Person类继承:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=gender

定义Student类时,只需要把额外的属性加上,例如score:

classStudent(Person):
def__init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score=score

一定要用 super(Student, self).__init__(name, gender) 去初始化父类,否则,继承自 PersonStudent 将没有 namegender

函数super(Student, self)将返回当前类继承的父类,即 Person ,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不需要写出(也不能写)

3.Python中判断类型

函数isinstance()可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在我们自定义的类,它们本质上都是数据类型。

假设有如下的 Person、StudentTeacher 的定义及继承关系如下:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderclassStudent(Person):
def__init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score=scoreclassTeacher(Person):
def__init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course=coursep=Person('Tim', 'Male')
s=Student('Bob', 'Male', 88)
t=Teacher('Alice', 'Female', 'English')

当我们拿到变量 p、s、t 时,可以使用 isinstance 判断类型:

>>>isinstance(p, Person)
True#p是Person类型>>>isinstance(p, Student)
False#p不是Student类型>>>isinstance(p, Teacher)
False#p不是Teacher类型

这说明在继承链上,一个父类的实例不能是子类类型,因为子类比父类多了一些属性和方法。

我们再考察 s

>>>isinstance(s, Person)
True#s是Person类型>>>isinstance(s, Student)
True#s是Student类型>>>isinstance(s, Teacher)
False#s不是Teacher类型

s 是Student类型,不是Teacher类型,这很容易理解。但是s 也是Person类型,因为Student继承自Person,虽然它比Person多了一些属性和方法,但是,把 s 看成Person的实例也是可以的。


这说明在一条继承链上,一个实例可以看成它本身的类型,也可以看成它父类的类型。

4.Python中多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 StudentTeacher ,并都写了一个 whoAmI() 方法:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderdefwhoAmI(self):
return'I am a Person, my name is %s'%self.nameclassStudent(Person):
def__init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score=scoredefwhoAmI(self):
return'I am a Student, my name is %s'%self.nameclassTeacher(Person):
def__init__(self, name, gender, course):
super(Teacher, self).__init__(name, gender)
self.course=coursedefwhoAmI(self):
return'I am a Teacher, my name is %s'%self.name

在一个函数中,如果我们接收一个变量 x,则无论该 x Person、Student还是 Teacher,都可以正确打印出结果:

defwho_am_i(x):
printx.whoAmI()
p=Person('Tim', 'Male')
s=Student('Bob', 'Male', 88)
t=Teacher('Alice', 'Female', 'English')
who_am_i(p)
who_am_i(s)
who_am_i(t)

运行结果:

IamaPerson, mynameisTimIamaStudent, mynameisBobIamaTeacher, mynameisAlice


这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。

由于Python是动态语言,所以,传递给函数 who_am_i(x)的参数 x 不一定是 Person 或 Person 的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可:

classBook(object):
defwhoAmI(self):
return'I am a book'

这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。

5.Python中多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。

多重继承的继承链就不是一棵树了,它像这样:

classA(object):
def__init__(self, a):
print'init A...'self.a=aclassB(A):
def__init__(self, a):
super(B, self).__init__(a)
print'init B...'classC(A):
def__init__(self, a):
super(C, self).__init__(a)
print'init C...'classD(B, C):
def__init__(self, a):
super(D, self).__init__(a)
print'init D...'

看下图:

image.png像这样,D 同时继承自 BC,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用__init__()方法时,A 虽然被继承了两次,但__init__()只调用一次:

>>>d=D('d')
initA...
initC...
initB...
initD...

多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。


举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有 多进程ForkingMixin 多线程ThreadingMixin两种。


要创建多进程模式的 TCPServer:

classMyTCPServer(TCPServer, ForkingMixin)
pass

如果没有多重继承,要实现上述所有可能的组合需要 4x2=8 个子类。

6.Python中获取对象信息

拿到一个变量,除了用 isinstance() 判断它是否是某种类型的实例外,还有没有别的方法获取到更多的信息呢?

例如,已有定义:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderclassStudent(Person):
def__init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score=scoredefwhoAmI(self):
return'I am a Student, my name is %s'%self.name

首先可以用 type() 函数获取变量的类型,它返回一个 Type 对象:

>>>type(123)
<type'int'>>>>s=Student('Bob', 'Male', 88)
>>>type(s)
<class'__main__.Student'>

其次,可以用 dir() 函数获取变量的所有属性:

>>>dir(123)   #整数也有很多属性...
['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]
>>>dir(s)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score', 'whoAmI']

对于实例变量,dir()返回所有实例属性,包括`__class__`这类有特殊意义的属性。注意到方法`whoAmI`也是 s 的一个属性。

如何去掉`__xxx__`这类的特殊属性,只保留我们自己定义的属性?回顾一下filter()函数的用法。

dir()返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() setattr( )函数了:

>>>getattr(s, 'name')  #获取name属性'Bob'>>>setattr(s, 'name', 'Adam')  #设置新的name属性>>>s.name'Adam'>>>getattr(s, 'age')  #获取age属性,但是属性不存在,报错:Traceback (mostrecentcalllast):
File"<stdin>", line1, in<module>AttributeError: 'Student'objecthasnoattribute'age'>>>getattr(s, 'age', 20)  #获取age属性,如果属性不存在,就返回默认值20:20

7.待更新...


七、定制类

  1. 简介

image.pngimage.pngimage.pngimage.png2.Python中 __str__和__repr__

如果要把一个类的实例变成 str,就需要实现特殊方法__str__()

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderdef__str__(self):
return'(Person: %s, %s)'% (self.name, self.gender)

现在,在交互式命令行下用 print 试试:

>>>p=Person('Bob', 'male')
>>>printp(Person: Bob, male)

但是,如果直接敲变量 p

>>>p<main.Personobjectat0x10c941890>

似乎__str__() 不会被调用。

因为 Python 定义了__str__()__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员。

有一个偷懒的定义__repr__的方法:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderdef__str__(self):
return'(Person: %s, %s)'% (self.name, self.gender)
__repr__=__str__

3.Python中 __cmp__

intstr 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()

classStudent(object):
def__init__(self, name, score):
self.name=nameself.score=scoredef__str__(self):
return'(%s: %s)'% (self.name, self.score)
__repr__=__str__def__cmp__(self, s):
ifself.name<s.name:
return-1elifself.name>s.name:
return1else:
return0

上述 Student 类实现了__cmp__()方法,__cmp__用实例自身self和传入的实例 s 进行比较,如果 self 应该排在前面,就返回 -1,如果 s 应该排在前面,就返回1,如果两者相当,返回 0。

Student类实现了按name进行排序:

>>>L= [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)]
>>>printsorted(L)
[(Alice: 77), (Bob: 88), (Tim: 99)]

注意: 如果list不仅仅包含 Student 类,则 __cmp__ 可能会报错。

任务

请修改 Student __cmp__ 方法,让它按照分数从高到底排序,分数相同的按名字排序。(如果list中数据类型不一样如何解决?)

classStudent(object):
def__init__(self, name, score):
self.name=nameself.score=scoredef__str__(self):
return'(%s: %s)'% (self.name, self.score)
__repr__=__str__def__cmp__(self, s):
ifFalse==isinstance(s, Student):
return-1return-cmp(self.score,s.score) orcmp(self.name,s.name)
L= [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
printsorted(L)

4.Python中 __len__

如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。

要让 len() 函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

例如,我们写一个 Students 类,把名字传进去:

classStudents(object):
def__init__(self, *args):
self.names=argsdef__len__(self):
returnlen(self.names)

只要正确实现了__len__()方法,就可以用len()函数返回Students实例的“长度”:

>>>ss=Students('Bob', 'Alice', 'Tim')
>>>printlen(ss)
3

5.Python中数学运算

Python 提供的基本数据类型 int、float 可以做整数和浮点的四则运算以及乘方等运算。

但是,四则运算不局限于int和float,还可以是有理数、矩阵等。

要表示有理数,可以用一个Rational来表示:

classRational(object):
def__init__(self, p, q):
self.p=pself.q=q

p、q 都是整数,表示有理数 p/q。

如果要让Rational进行+运算,需要正确实现__add__:

classRational(object):
def__init__(self, p, q):
self.p=pself.q=qdef__add__(self, r):
returnRational(self.p*r.q+self.q*r.p, self.q*r.q)
def__str__(self):
return'%s/%s'% (self.p, self.q)
__repr__=__str__

现在可以试试有理数加法:

>>>r1=Rational(1, 3)
>>>r2=Rational(1, 2)
>>>printr1+r25/6

6.Python中类型转换

Rational类实现了有理数运算,但是,如果要把结果转为 int float 怎么办?

考察整数和浮点数的转换:

>>>int(12.34)
12>>>float(12)
12.0

如果要把 Rational 转为 int,应该使用:

r=Rational(12, 5)
n=int(r)

要让int()函数正常工作,只需要实现特殊方法__int__():

classRational(object):
def__init__(self, p, q):
self.p=pself.q=qdef__int__(self):
returnself.p// self.q

结果如下:

>>>printint(Rational(7, 2))
3>>>printint(Rational(1, 3))
0

同理,要让float()函数正常工作,只需要实现特殊方法__float__()

7.Python中 @property

考察 Student 类:

classStudent(object):
def__init__(self, name, score):
self.name=nameself.score=score

当我们想要修改一个 Studentscroe 属性时,可以这么写:

s=Student('Bob', 59)
s.score=60

但是也可以这么写:

s.score=1000

显然,直接给属性赋值无法检查分数的有效性。

如果利用两个方法:

classStudent(object):
def__init__(self, name, score):
self.name=nameself.__score=scoredefget_score(self):
returnself.__scoredefset_score(self, score):
ifscore<0orscore>100:
raiseValueError('invalid score')
self.__score=score

这样一来,s.set_score(1000) 就会报错。

这种使用 get/set 方法来封装对一个属性的访问在许多面向对象编程的语言中都很常见。

但是写 s.get_score()s.set_score() 没有直接写 s.score 来得直接。

有没有两全其美的方法?----有。

因为Python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用:

classStudent(object):
def__init__(self, name, score):
self.name=nameself.__score=score@propertydefscore(self):
returnself.__score@score.setterdefscore(self, score):
ifscore<0orscore>100:
raiseValueError('invalid score')
self.__score=score

注意:第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。

现在,就可以像使用属性一样设置score了:

>>>s=Student('Bob', 59)
>>>s.score=60>>>prints.score60>>>s.score=1000Traceback (mostrecentcalllast):
  ...
ValueError: invalidscore

说明对 score 赋值实际调用的是 set方法

8.Python中 __slots__

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。


如果要限制添加的属性,例如,Student类只允许添加 name、genderscore 这3个属性,就可以利用Python的一个特殊的__slots__来实现。


顾名思义,__slots__是指一个类允许的属性列表:

classStudent(object):
__slots__= ('name', 'gender', 'score')
def__init__(self, name, gender, score):
self.name=nameself.gender=genderself.score=score

现在,对实例进行操作:

>>>s=Student('Bob', 'male', 59)
>>>s.name='Tim'#OK>>>s.score=99#OK>>>s.grade='A'Traceback (mostrecentcalllast):
  ...
AttributeError: 'Student'objecthasnoattribute'grade'

__slots__的目的是限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用__slots__也能节省内存。

9.python中 __call__

在Python中,函数其实是一个对象:

>>>f=abs>>>f.__name__'abs'>>>f(-123)
123

由于 f 可以被调用,所以,f 被称为可调用对象。

所有的函数都是可调用对象。

一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()

我们把 Person 类变成一个可调用对象:

classPerson(object):
def__init__(self, name, gender):
self.name=nameself.gender=genderdef__call__(self, friend):
print'My name is %s...'%self.nameprint'My friend is %s...'%friend

现在可以对 Person 实例直接调用:

>>>p=Person('Bob', 'male')
>>>p('Tim')
MynameisBob...
MyfriendisTim...

单看 p('Tim') 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。

10.待更新...

目录
相关文章
|
JSON 负载均衡 JavaScript
带你读《Elastic Stack 实战手册》之65:——3.5.19.1.Elasticsearch语言开发(Python)(上)
带你读《Elastic Stack 实战手册》之65:——3.5.19.1.Elasticsearch语言开发(Python)(上)
256 1
|
5月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
60 4
|
5月前
|
消息中间件 网络协议 网络安全
Python Socket编程:打造你的专属网络通道,基础篇与进阶篇一网打尽!
【7月更文挑战第26天】在网络编程领域,Python以简洁语法和强大库支持成为构建应用的首选。Socket编程为核心,实现计算机间的数据交换。
74 1
|
5月前
|
消息中间件 网络协议 网络安全
解锁Python Socket新姿势,进阶篇带你玩转高级网络通信技巧!
【7月更文挑战第26天】掌握Python Socket后,探索网络通信高级技巧。本指南深化Socket编程理解,包括非阻塞I/O以提升并发性能(示例使用`select`),SSL/TLS加密确保数据安全,以及介绍高级网络协议库如HTTP、WebSocket和ZeroMQ,简化复杂应用开发。持续学习,成为网络通信专家!
51 0
|
Linux 文件存储 Python
文件和目录操作|多进程和多线程【python进阶篇】
文件和目录操作|多进程和多线程【python进阶篇】
文件和目录操作|多进程和多线程【python进阶篇】
Python面向对象编程-进阶篇
面向对象三大特性:封装、继承、多态,面向对象中的变量:类变量、成员变量、局部变量,类中的私有方法和私有属性,类的三类方法:实例方法、类方法、静态方法。
Python面向对象编程-进阶篇
|
资源调度 算法 计算机视觉
python opencv 图像处理进阶篇(二)
python opencv 图像处理进阶篇(二)
130 0
|
机器学习/深度学习 算法 计算机视觉
python opencv 图像处理进阶篇(一)
python opencv 图像处理进阶篇(一)
118 0
|
存储 人工智能 运维
带你读《Elastic Stack 实战手册》之65:——3.5.19.1.Elasticsearch语言开发(Python)(下)
带你读《Elastic Stack 实战手册》之65:——3.5.19.1.Elasticsearch语言开发(Python)(下)
139 0
|
Python
[Python公开课]零基础玩转Python进阶篇----第八节:Python中的文件操作
[Python公开课]零基础玩转Python进阶篇----第八节:Python中的文件操作
119 0
下一篇
DataWorks