日期:20170916
抒情(前言了啦)
噢噢!今天一下子差不多看完Python类的知识点,但是没怎么练习,很多都没记住,感觉好多啊。印证了一句话,看一百页书,不如写一行代码。
比Java的类难学。(虽然我是个Java小白,嘻嘻,但是学Java类时都是,略略略。。。)
原因可能是Python作为集合多种语言语义的上层语言,为配合不同语言的人的编程习惯,所以有很多种编程语法。例如:
1、实例可以直接在加变量(属性)。
2、若你想限制实例不能直接加属性可以用__slots__。
3、内部属性用双下划线__来定义。(虽然只是个骗局。为什么所骗局呢?以后有机会再写一篇博客讨论)
。。。
Python的理念是,能写一行的代码,绝不写十行。能写简单的,绝不写复杂的(类似,原话不知道)。
但是上面两句话并不相容,或许只存在超高级语言中。(很意味深长的一句话)
可以说,Python编程是习惯约束的。在Python中你完全可以按自己习惯来写代码。但是要想看懂别人的代码,或者想别人看懂你的代码,建议(必须)还是要学一下常用的语法。
最简单的类
现在先讲最简单的类定义。
代码,
#!/usr/bin/python
class student(object):
def __init__(self, name, age):
self.name=name
self.age=age
def print_info(self):
print("Student")
print("Name: %s"%(self.name))
print("Age: %s"%(self.age))
Stu1=student("Penx", 13)
Stu1.print_info()
运行,
[penx@ali01 python2]$ ./simple_class.py
Student
Name: Penx
Age: 13
[penx@ali01 python2]$
一个类的实例,至少要有属性,要有方法。就像现实中,所有东西都有属性,有行为。(非生物的行为,比如,鸡蛋和石头都可以被敲,鸡蛋容易被敲破,石头则很难被敲碎,被敲后的表现则是鸡蛋和石头的行为)
代码分析,
1、第13行,是创建student类的实例Stu1,而student类是第3行以class开头定义的。第14行,是调用实例的方法。
2、第3行括号里是写student继承的类。如果没有想要继承的类,可以写object。object是最原始的类,是其他全部类的“始祖”。
3、第4行和第8行以def开始的是定义类方法。
4、_ _init_ _()是创建实例时,自动调用的方法。(__init__是以双下划线开头和以双划线结尾)
5、类方法的第一个参数,习惯用名字self。self是实例自己本身。
剖析,(都是我自己的测试)
1、定义类时,不继承任何类,即连object都不继承,括号里是空的。运行成功。(网上说python3定义类时,默认继承object类)
2、不写__init__()。运行成功。应该是调用了父类的__init__()。
3、定义类方法时,第一个参数不是名字self。运行成功。猜测,调用类方法时,第一个参数默认传入实例本身。
4、定义类方法时,没有参数。调用时,传入一个参数。运行失败。
报错如下,
TypeError: print_info() takes exactly 1 argument (2 given)
证明了第3点的猜想是正确的。
实例可自增加属性(不建议用)
实例可以自己增加属性的。
代码,
#!/usr/bin/python
class student():
def __init__(self, name, age):
self.name=name
self.age=age
def print_info(self):
print("Student")
print("Name: %s"%(self.name))
print("Age: %d"%(self.age))
Stu1=student("Penx", 13)
Stu1.print_info()
Stu1.height=15 #实例可以在创建后自增加属性
print("Height: %d"%Stu1.height)
运行,
[penx@ali01 python2]$ ./add_new_attr.py
Student
Name: Penx
Age: 13
Height: 15
[penx@ali01 python2]$
分析,
除了类定义时的实例属性name和age,我们还可以在创建属性时,增加新的属性height(第16行)。
但是我不建议在定义完一个类后,实例自己增加属性。你想想,你在用别人编写的类时,会增加属性吗?通常不会吧。
剖析,
1、可以知道,实例的属性是在__init__()创建的。
实例可自增加方法(不建议用)
这条和“实例可自增加属性”一样,不建议用的理由也一样。
(大家自行脑补)
使用__slots__限制实例属性(少用)
哇哇,在python2中貌似没有啊,不知道python3有没有用。
我看的是python3的教程,用的是python2。
算了,我不打算用它,就不纠结了。
前面也说过,python很多语法都是习惯约定的。我们在用实例时,应不轻易增加属性和方法。
所以,以后看别人代码,知道有这条规则就好。
注意:
1、slots定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
2、除非在子类中也定义slots,子类实例允许定义的属性就是自身的slots加上父类的slots
实例属性可直接修改
这条很多类编程语言都可以,就是在创建实例后,直接修改实例属性。
实例数据封装
如果用上面那条,实例属性直接修改,那么属性值的修改就限制。
代码,
#!/usr/bin/python
class student():
def __init__(self, name, age):
self.name=name
self.age=age
def print_info(self):
print("Student")
print("Name: %s"%(self.name))
print("Age: %s"%(self.age))
Stu1=student("Penx", 13)
Stu1.name=2017
Stu1.age="Alice"
Stu1.print_info()
运行,
[penx@ali01 python2]$ ./example_wrong_attr.py
Student
Name: 2017
Age: Alice
[penx@ali01 python2]$
代码中,类定义没有做任何限制,直接修改就很任意,name修改为数字,age修改为字符串。
所以,我们修改属性时,可以把属性的修改通过方法来封装,那么就可以在修改属性时,检查属性数据的合法性。
使用@property进行属性数据封装(Python2不可用)
使用常规的封装,在思维上不怎么人类。因为我们都喜欢直接修改属性,而不是用方法来修改。
那么有没有一种既可以直接修改属性,又可以检查属性的合法呢?
答案是,有,那就是使用@property。
代码,
/usr/bin/python3 #python
class student():
def __init__(self, name, age):
self.name=name
self.age=age
@property
def name(self):
return self.name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("name must be a string!")
def print_info(self):
print("Student")
print("Name: %s"%(self.name))
print("Age: %s"%(self.age))
Stu1=student(23, 13)
Stu1.print_info()
在第8行开始,定义了方法name,然后我们在第7行用装饰器@property装饰了name,使name能返回一个值。@property还创建了另一个装饰器@name.setter。
13行的方法name是检查name修改时,传入的值是否合法,它被12行的@name.setter装饰,使name的“=”重载(赋值重载)。
利用以上两点,实例在创建后,就可以直接获取和传入实例的属性被检查。
注,
1、@property是把方法装饰成读取属性。
2、@xxx.setter是把方法装饰成修改属性。
3、装饰的方法名字一定要一样。(测试过,不一样的话,运行失败。)
4、可以只用@property,那样属性就变成只读属性。
剖析,
@property的内部使用了装饰器和类定制的知识。(待我过一段时间看官方文档时,再开另一篇博客详细实现@property)