Python基本思想——面向对象编程

以学生为一个例子来说:

我们在编程时思考的不是程序的执行流程,而是对象(即学生)

学生们都有什么共同的属性(property)?

  • 字典里的姓名(name)和分数(score)都是学生的属性

打印学生的成绩:

  • 先创建学生对象
  • 再让对象打印成绩
  • 注:打印成绩可以让对象自行操作

面向对象的设计思想是抽象出Class,根据Class创建Instance。

#类相当于模版:在创建实例的时候,可以把我们认为模版必须有的属性填写进去。

class Student():
	#类的内部实现
	def __init__(self, name, score):
	#定义一个构造函数__init__,在创建实例的时候就把name,score等属性绑上去。__init__的第一个参数永远是self,表示创建的实例本身:因为self指向 创建的实例本身, __init__把各种属性绑定到self,即绑定到实例。
		self.name = name 
		self.score = score
	def print_score(self):#类的方法(函数)
		print('%s: %s' % (self.name, self.score))

class Student:
	def __init__(aabb, name, score):
		aabb.name = name 
		aabb.score = score
	def print_score(aabb):
		print('%s: %s' % (aabb.name, aabb.score))
#注意:
# self只是一个习惯
# 实际名字是可以变化的,不一定是self
# 建议编写代码时仍以self作为方法的第一个参数名字
	

#实例化对象:
michael = Student('Michael','A') 
bob = Student('Bob','C')
#类的方法:调用对象对应的关联函数
michael.print_score()#Michael: A 
bob.print_score() #Bob: C
#类的属性 
print(michael.name) #Michael 
print(michael.score) #A
print(bob.name) #Bob 
print(bob.score) #C

实例属性:

一般在构造函数__init__中定义;定义和使用时必须以self作为前缀

属于实例,只能通过对象名访问

类属性:

类中所有方法之前定义的数据成员

通过对象名或类名访问

#全部可访问
class Student:
	university = "Shenzhen University" 
	def __init__(self, name, score):
		self.name = name 
		self.score = score
#实例属性
print(bob.name,bob.score,bob.university,Student.university)
#Bob C Shenzhen University Shenzhen University

#类属性
print(Student.name)
#AttributeError: type object 'Student' has no attribute 'name'

#修改类属性
Student.university = 'SZU’ 
print(michael.university) #SZU 
print(bob.university) #SZU

#增加类属性
Student.major = 'CS’ 
print(michael.major) #CS 
print(bob.major) #CS

#修改实例属性 
michael.score = 'B'
bob.name = 'BOB'

#增加实例属性
michael.id = 1001 
print(michael.id) #1001 
print(bob.id) #AttributeError

#动态增加成员方法
import types
michael.change_score = types.MethodType(change_score,michael)
#这个函数只能够改变michael的成绩
michael.change_score('B') 
print(michael.score) #B

bob.change_score('D')
print(bob.score) #AttributeError

del michael.change_score #删除

#应用:管理复杂的用户分类:不同用户组具有不同的行为和权限,并且可能会经常改变

私有变量

可以把属性的名称前加上两个下划线__

实例的变量名如果以__开头,代表一个私有变量(private)

只有内部可以访问,外部不能访问!

一般通过调用对象的公有成员方法来访问

#类内可访问,类外不可访问
class Student:
	def __init__(self, name, score):
		self.__name = name 
		self.__score = score
	def show_score(self): #显示 
		return self.__score
	def change_score(self,score): #修改 
		self.__score = score

michael = Student('Michael','A') 
bob = Student('Bob','C')

print(michael.__score)#AttributeError: 'Student' object has no attribute '__score'
print(bob.__name)#AttributeError: 'Student' object has no attribute '__name’
#通过类内的访问来显示michael的成绩
print(michael.show_score()) #A
#通过类内的方法来改变michael的成绩,这个改变是的确将类内的信息改变了的
michael.change_score('B') 
print(michael.show_score()) #B

#另外一种修改方式
michael=Student('Michael','A') 
michael.__score ='B'
print(michael.__score) #B,但是其实并未修改类内的变量,内部的变量其实被编译器认为是_Student__ score,这里的外部代码是给michael新增了一个__ score变量,并进行了新的赋值,并未改变类内的信息。
print(michael.show_score()) #A

#另一种声音:Python中不存在严格意义上的私有成员。可以通过_Student__name来访问__name变量
print(michael.__name) # error 
print(michael.__score) # error 
print(michael._Student__name) #Michael 
print(michael._Student__score) #A

变量名的定义

xxx:系统定义的特殊成员、特殊变量、可以直接访问的

__xxx:私有成员,只有类对象自己能访问,但在对象外部可以通过“对象名._类名__xxx”这样的特殊方式来访问

_xxx:受保护成员,不能用'from module import *'导入

注意点:类属性中的可变类型

class Dog:
	motion = [] #属于可变类型的列表
	def __init__(self, name): 
		self.name = name
	def add_motion(self, motion): 
		self.motion.append(motion)

d1 = Dog('Fido')
d2 = Dog('Buddy') 
d1.add_motion('run') 
d2.add_motion('sit')
print(d1.motion) # ['run', 'sit'],这里就是没有将motion进行分类,从而导致了d1和d2动作都放在了一起。

#正确的做法:
class Dog:
	def __init__(self, name): 
		self.name = name
		self.motion = []
	def add_motion(self, motion): 
		self.motion.append(motion)

d1 = Dog('Fido')
d2 = Dog('Buddy') 
d1.add_motion('run') 
d2.add_motion('sit')
print(d1.motion)#['run']

公有方法、私有方法

#例一:学生
class Student:
	def __init__(self, name, score):
		self.__name = name 
		self.__score = score
	def show_score(self): #显示 
		return self.__score
	def change_score(self,score): #修改 
		self.__score = score

michael = Student('Michael','A') 
#无法在外部直接调用类内的方法
michael.__change_score('B')
#AttributeError: 'Student' object has no attribute '__change_score'

#正确的调用方法
michael._Student__change_score('B') #这里要用_Student绕过去,将私有方法变为公有方法,即可改变。
print(michael.show_score()) #B
#例二:宠物
class Pet:
	def __init__(self, name, age=0):
        self.__name = name 
		self.__age = age
	def get_name(self): 
		return self.__name
	def get_age(self): 
		return self.__age
	#内置函数__str__定义当我们打印Pet实例时要发生的操作。在这里我们重写它来打印宠物的名字。
	def __str__(self):
		return "This pet’s name is " + str(self.__name)

mypet1 = Pet('Ben') 
print(mypet1)#This pet’s name is Ben
print(mypet1.get_name()) #Ben 
print(mypet1.get_age()) #0

mypet2 = Pet(age=2,name='Bob') 
print(mypet2.get_name()) #Bob 
print(mypet2.get_age()) #2

类的构造函数(constructor) 是__init__(),一般用来为数据成员设置 初值或进行其他必要的初始化工作,在创建对象时被自动调用和执行。

类的析构函数(destructor) 是__del__(),一般用来释放对象占用的资 源,在Python删除对象和收回对象空间时被自动调用和执行。

面向对象与面向过程

#问题:一个列表中每个元素都加上一个相同数字?

#面向过程:
lst = [1,2,3,5]
[(item + 2) for item in lst]

#面向对象:重新编写类 + 重写特殊方法来实现运算符重载(override)
class mylist:
	def __init__(self,lst=[]):
		self.__size = len(lst) 
		self.__list = lst
	def add_item(self,item): 
		self.__list.append(item)

	#未将面向对象进行到底的写法:
	def __add__(self, n): #重写内置函数__add__ 实现运算符重载
		temp = mylist()
		for item in self.__list: 
			temp.add_item(item+n)
		return temp. __list     #这个. __list不纯粹
		#如果这里将.__list去除,则print(lst+2)无法正确显示,会报错:<__main__.mylist object at 0x7fad30106160>

lst = mylist([1,2,3,5])
print(lst+2) #[3,4,5,7]

	#完全体现面向对象思维的写法:
class mylist:
	def __init__(self,lst=[]):
		self.__size = len(lst) 
		self.__list = lst
	def add_item(self,item): 
		self.__list.append(item)
	def __add__(self, n): 
		temp = mylist()
		for item in self.__list: 
			temp.add_item(item+n)
		return temp
	def __str__(self):
		return str(self.__list)

lst = mylist([1,2,3,5])
print(lst+2) #[3,4,5,7],这里返回的是整个对象

#结论:要体现最终的结果,最好使用def __str__(self):这个函数
#例二:点类
class Point:
	def __init__(self, x=0, y=0):
		self.x = x 
		self.y = y
	def __add__(self, other):
		return Point(self.x + other.x, self.y + other.y)
	def __str__(self):
		return "Point({0}, {1})".format(self.x, self.y)

A = Point(3, 4)
B = Point(-1, 2)
str(A) # => Point(3, 4) 
print(A + B) # => Point(2, 6)

继承

继承是为代码复用和设计复用而设计的,是面向对象程序设计的重要特性之一。设计一个新类时,如果可以继承一个已有的设计良好的类然后进行二次开发,无疑会大幅度减少开发工作量

#继承——子类与父类
class Animal(object):#父类
      def run(self):
				print('Animal is running...')
class Dog(Animal):#子类
    pass
class Cat(Animal):#子类
    pass
#子类获得了父类的全部功能。由于Animial实现了run()方法
#因此,Dog和Cat作为它的子类,自动拥有了run()方法:
dog = Dog()
dog.run() #Animal is running...
cat = Cat()
cat.run() #Animal is running...
#但是这里我们也会发现Dog和Cat都输出Animal,显然是不那么合理的

#多态(Polymorphism)的应用:按字面的意思就是“多种状态”
class Dog(Animal):
     def run(self):
				print('Dog is running...')
class Cat(Animal):
     def run(self):
				print('Cat is running...')
dog = Dog()
dog.run() #Dog is running...
#当子类和父类都存在相同的run()方法时,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。
cat = Cat()
cat.run() #Cat is running...

#多态的应用:对子类增加父类没有的方法
class Dog(Animal):
     def run(self):
				print('Dog is running...')
		 def eat(self):
				print('Dogs like meat...')
class Cat(Animal):
     def run(self):
				print('Cat is running...')
		 def eat(self):
				print('Cats like fish...')

dog = Dog()
dog.eat() #Dogs like meat...
cat = Cat()
cat.eat() #Cats like fish...

#子类判断函数issubclass(class, classinfo)
class Animal(object):#父类
    pass
class Dog(Animal):#子类
    pass
class Cat(Animal):#子类
    pass
issubclass(Animal, Animal) #T
issubclass(Dog, Animal)  #T
issubclass(Cat, Animal)  #T
issubclass(Animal, object)  #T
issubclass(Dog, object)  #T
issubclass(int, Animal) #F
issubclass(list, Animal) #F
#例二:对于子类和父类中的函数要进行对应的区别,不能函数名相同
class Father:
	#这个代码是错误的,__init__调用update (public): Python找到的是Son的update.
	def __init__(self, iterable=[1,2,3]):
		self.items_list = [] 
		self.update(iterable)
	
	def update(self, iterable): 
		for item in iterable:
			self.items_list.append(item)

class Son(Father):
	def update(self, keys, values):
		for item in zip(keys, values): 
			self.items_list.append(item)

a=Father() 
b=Son()

#修正代码:
class Father:
	def __init__(self, iterable=[1,2,3]):
		self.items_list = [] 
		self.update_father(iterable)
	
	def update_father(self, iterable): 
		for item in iterable:
			self.items_list.append(item)

class Son(Father):
	def update_son(self, keys, values):
		for item in zip(keys, values): 
			self.items_list.append(item)

a=Father() 
b=Son()
#exercise
class A(object):
	def __init__(self):
		self.__private()
		self.public() 
	def __private(self):
		print('__private() method in A') 
	def public(self):
		print('public() method in A')

class B(A):
	def __private(self):
		print('__private() method in B') 
	def public(self):
		print('public() method in B') 
b=B() 
#输出:
#__private() method in A 
#public() method in B

多态的好处:不用一次性建很多新的类,只需要建立一个公用的类,然后不断进行实体化即可。

#练习
class Animal(object): 
	def show(self):
		print('I am an animal.')
class Cat(Animal):
	def show(self): 
		print('I am a cat.')
class Dog(Animal): 
	def show(self):
		print('I am a dog.')
class Tiger(Animal): 
	def show(self):
		print('I am a tiger.')
class Test(Animal):
	pass
x = [item() for item in(Animal, Cat, Dog, Tiger, Test)]
	for item in x: 
		item.show()
输出:
I am an animal. 
I am a cat.
I am a dog.
I am a tiger. 
I am an animal.
class Canadian:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    def print_name(self):
        print("I'm {} {}, Canadian!".format(self.first_name,self.last_name))
class American:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    def print_name(self):
        print("I'm {} {}, American!".format(self.first_name, self.last_name))
class BritishColumbian(Canadian, American):
    pass
michael = BritishColumbian("Michael", "Cooper")
michael.print_name()#I'm Michael Cooper, Canadian!
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值