面向对象编程简单说就是将问题抽取成为对象集合,再进行编程实现问题的解决。
一个特别有趣的面向对象的介绍: 上和 下。
定义类与创建实例
利用【class】来定义类:
class Person(object):
pass
在Python中类名一般以大写字母开头,后面的(object)表示该类是从哪个类继承下来的。定义一个Person类,并创建出两个实例,打印出实例:
class Person(object):
pass
xiaoming = Person()
xiaohong = Person()
print(xiaoming)
print(xiaohong)
>> <__main__.Person object at 0x000001FEE1E22EB8>
>> <__main__.Person object at 0x000001FEE1E222E8>
可以给一个实例变量绑定属性,比如,给xiaoming和xiaohong绑定name、grade和birth:
class Person(object):
pass
xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.birth = '2018-05-23'
xiaohong = Person()
xiaohong.name = 'Xiao Hong'
xiaohong.grade = 2 # 绑定的属性可以不同
print(xiaoming.name) # 打印实例的相应属性
print(xiaoming.birth)
print(xiaohong.name)
print(xiaohong.grade)
>> Xiao Ming
>> 2018-05-23
>> Xiao Hong
>> 2
在创建实例的时候,可以把一些我们认为必须绑定的属性强制填写进去,为Person类添加一个特殊的__init__() 初始化方法,在创建实例的时候就把name、birth、grade绑定:
class Person(object):
def __init__(self, name, birth, grade):
self.name = name
self.birth = birth
self.grade = grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)
print(xiaoming.name)
print(xiaohong.birth)
>> Xiao Ming
>> 2010-10-24
注:init 前后分别有两个下划线
当然,此方法也可以接受任意关键字参数,并把他们都作为属性赋值给实例:
class Person(object):
def __init__(self, name, birth, grade, **kw):
self.name = name
self.birth = birth
self.grade = grade
for k, v in kw.items():
setattr(self, k, v)
xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')
print(xiaoming.birth)
print(xiaoming.job)
print(xiaohong.gender)
>> 2018-05-23
>> Student
>> Female
封装
面向对象编程的一个重要特点就是数据封装。封装之后,从外部看对应的类就只知道创建实例需要给出哪些属性,但具体如何“工作”的是不知道的,比如我们可以通过一个函数来打印一个人的出生日期:
class Person(object):
def __init__(self, name, birth, grade, **kw):
self.name = name
self.birth = birth
self.grade = grade
for k, v in kw.items():
setattr(self, k, v)
xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')
def print_score(std):
print('%s: %s' % (std.name, std.birth))
print_score(xiaoming)
>> Xiao Ming: 2018-05-23
将数据封装后如下:
class Person(object):
def __init__(self, name, birth, grade, **kw):
self.name = name
self.birth = birth
self.grade = grade
for k, v in kw.items():
setattr(self, k, v)
def print_birth(std):
print('%s: %s' % (std.name, std.birth))
xiaoming = Person('Xiao Ming', '2018-05-23', 0, job='Student')
xiaohong = Person('Xiao Hong', '2010-10-24', 2, gender='Female')
xiaoming.print_birth()
>>Xiao Ming: 2018-05-23
访问限制
对于之前我们所定义的class是可以通过外部代码自由修改的,如:
class Person(object):
def __init__(self, name, birth, grade):
self.name = name
self.birth = birth
self.grade = grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)
print(xiaoming.grade)
xiaoming.grade = 9
print(xiaoming.grade)
>> 0
>> 9
那么如何让内部的属性不被外部访问呢?Python对属性的权限控制是通过以“双下划线开头【__】”实现的,这样的实例变量的属性就只有内部可以访问,外部不能访问,所以来操作一波:
class Person(object):
def __init__(self, name, birth, grade):
self.__name = name
self.__birth = birth
self.__grade = grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)
print(xiaoming.__grade)
>> Traceback (most recent call last):
File "F:/Python/filter/sorted.py", line 158, in <module>
print(xiaoming.__grade)
AttributeError: 'Person' object has no attribute '__grade'
可见,此时已经无法从外部访问实例变量的属性了,就让代码保护起来了,但是如果想获取该怎么办呢?当当当,可以给类增加【get_grade】函数(这个函数就叫做实例的方法)啊:
class Person(object):
def __init__(self, name, birth, grade):
self.__name = name
self.__birth = birth
self.__grade = grade
def get_grade(self): # 实例的方法的第一个参数永远是self
return self.__grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)
print(xiaoming.get_grade()) # self不需要显式传入
>> 0
那么,可以在外部访问,自然也是可以做到在外部修改的,在增加一个【set_grade】就可以了:
class Person(object):
def __init__(self, name, birth, grade):
self.__name = name
self.__birth = birth
self.__grade = grade
def get_grade(self):
return self.__grade
def set_grade(self, grade):
self.__grade = grade
xiaoming = Person('Xiao Ming', '2018-05-23', 0)
xiaohong = Person('Xiao Hong', '2010-10-24', 2)
print(xiaoming.get_grade())
xiaoming.set_grade(9)
print(xiaoming.get_grade())
>> 0
>> 9
虽然以单“下划线开头的属性【_xxx】”也可以被外部访问和修改,但是,按照习惯,他们不应该被外部访问。
注意:变量名以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量。
实际上方法也是一个属性,所以它也可以动态地添加到实例上,用【types.MethodType() 】可以把一个函数变成一个方法:
import types
def fn_get_grade(self):
if self.score >= 80:
return 'A'
if self.score >= 60:
return 'B'
return 'C'
class Person(object):
def __init__(self, name, score):
self.name = name
self.score = score
xiaoming = Person('Xiao Ming', 90)
xiaohong = Person('Xiao Hong', 60)
xiaoming.get_grade = types.MethodType(fn_get_grade, xiaoming)
print(xiaoming.get_grade())
print(xiaohong.get_grade()) # xiaohong实例并没有绑定get_grade
>> A
>> AttributeError: 'Person' object has no attribute 'get_grade'
和属性类似,方法也分实例方法和类方法。那么,如何在class中定义类方法呢?答案是通过一个【@classmethod】来实现让该方法绑定在类上,类方法的第一个参数将传入类本身,通常将参数名命名为 cls:
class Person(object):
count = 0
@classmethod
def how_many(cls):
return cls.count
def __init__(self, name):
self.name = name
Person.count = Person.count + 1
print(Person.how_many())
xiaoming = Person('Xiao Ming')
print(Person.how_many())
xiaohong = Person('Xiao Hong')
print(Person.how_many())
print(xiaoming.how_many())
>> 0
>> 1
>> 2
>> 2
创建类属性
类是模板, 而实例则是根据类创建的对象。
绑定在一个实例上的属性不会影响其他实例,但是如果给类绑定一个属性,则所有的实例均可以访问这个属性,以下用给Person绑定一个China的属性为例:
class Person(object):
L = 'China'
def __init__(self, name):
self.name = name
xiaoming = Person('Xiao Ming')
print(Person.L)
print(xiaoming.L)
>> China
>> China
我们得知:如果给类绑定一个的属性,则这个类下创建的实例都将拥有这个属性,当然因为Python是动态语言,所以类的属性也是可以动态添加和修改的。
既然类和实例都可以绑定属性,那么如果两个属性的名字冲突了怎么办?例:
class Person(object):
L = 'China'
def __init__(self, name):
self.name = name
xiaoming = Person('Xiao Ming')
xiaohong = Person('Xiao Hong')
print(Person.L)
print(xiaoming.L)
print(xiaohong.L)
xiaoming.L = 'Earth' # 给xiaoming绑定一个L属性
print(Person.L)
print(xiaoming.L)
print(xiaohong.L)
>> China
>> China
>> China
>> China
>> Earth
>> China
我们发现,结果只有xiaoming的属性改变了。即证明当我们给xiaoming绑定一个与类同名字的属性时,它只是改变了xiaoming自己的L属性,而类的L属性并没有被改变。可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。当把xiaoming的L属性删掉之后,xiaoming的L属性就又变成了‘China’:
...
del xiaoming.L
print(xiaoming.L)
>> China
所以,在实例上是无法修改类属性的。
拖了好久,用了好长时间学习,实在是基础太差,但我一直在努力,加油