面向对象
9、面向对象
类和实例
访问限制
# -*- coding: utf-8 -*-
import sys
'''
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def print_score(self):
print('%s: %s' % (self.name, self.age))
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
'''
class Student(object):
#如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,
# 实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
def __init__(self, name, score):
self.__name = name
self.__score = score
#如果外部代码要获取name和score怎么办?可以给Student类增加get_name和get_score这样的方法:
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
def get_name(self):
return self.__name
def get_score(self):
return self.__score
#如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法
def set__score(self,score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
lisa = Student('Lisa', 99)
bart = Student('Bart', 59)
#不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:
print(lisa._Student__name)
class StudentPlus(object):
def __init__(self, name, gender):
self.__name = name
self.__gender = gender
#外部访问返回gender
def get_gender(self):
return self.__gender
#外部修改gender
def set_gender(self,gender):
self.__gender = gender
bart = StudentPlus('Bart', 'male')
if bart.get_gender() != 'male':
print('测试失败1!')
else:
bart.set_gender('female')
if bart.get_gender() != 'female':
print('测试失败2!')
else:
print('测试成功!') # -*- coding: utf-8 -*-
import sys
'''
你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
'''
#父类
#当我们定义一个class的时候,我们实际上就定义了一种数据类型。
# 我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样:
class Animal(object):
def run(self):
print('Animal is running...')
#子类
#当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),
#在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态
class Dog(Animal):
def run(self):
print('Dog is running...')
def eat(self):
print('Eating meat...')
class Cat(Animal):
def run(self):
print('Cat is running...')
class Tortoise(Animal):
def run(self):
print('Tortoise is running slowly...')
#对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
#对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:
class Timer(object):
def run(self):
print('Start...')
#子类实例化
dog = Dog()
cat = Cat()
#这个函数接受一个Animal类型的变量:
def run_twice(animal):
animal.run()
animal.run()
#在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类
print(isinstance(3, list))
print(run_twice(Animal()))
print(run_twice(Tortoise()))
print(run_twice(dog))
print(dog.run())
print(cat.run())
#继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。
#动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。 #1、获取对象信息
#用type判断
print(type(8))
#isinstance还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple
print(isinstance([1, 2, 3], (list, tuple)))
#dir 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
print(dir('ABC'))
# 类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度
print('abc'.__len__())
# 我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:
class MyDog(object):
def __len__(self):
return 100
dog = MyDog()
print(len(dog))
# 剩下的都是普通属性或方法,比如lower()返回小写的字符串:
print('ER'.lower())
#仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
class MyObject(object):
def __init__(self):
self.x=10
def power(self):
return self.x*self.x
obj=MyObject()
#设置对象属性
hasattr(obj, 'x') # 有属性'x'吗?
hasattr(obj,'y')
setattr(obj,'y',19) # 设置一个属性y
getattr(obj,'y') # 获取属性y
print(obj.y) # 获取属性y内容
#如果试图获取不存在的属性,会抛出AttributeError的错误:
#也可以获得对象的方法:
print(hasattr(obj, 'power'))# 有属性'power'吗?
print(getattr(obj, 'power')) # 获取属性'power'
# 2、实例属性和类属性
class Student(object):
name = 'Student'
s = Student() # 创建实例s
print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
print(Student.name) # 打印类的name属性
s.name = 'Michael' # 给实例绑定name属性
print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
del s.name # 如果删除实例的name属性
print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
#3、__slots
# 如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
# 为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
#使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
#除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score = 9999
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
#x3=s.score = 99 # 绑定属性'score'
#多态
class A:
def say(self):
print('A')
class B:
def say(self):
print('B')
def say(obj):
obj.say()
def say(obj):
obj.say()
a=B()
print(say(a))
# 4\@property
高阶函数

