Python - 软件对象
目录
__init__(self) 构造器方法(constructor method)
Python与Java一样都是面向对象编程(Object-oriented programming, OOP)。
单词:
critter /ˈkrɪtə $ -ər/ noun
a creature, especially an animal 生物;〔尤指〕动物
最终会以Critter Caretaker程序来展示 面向对象编程。Criter Caretaker程序的功能是让用户去照顾自己的虚拟宠物。用户需要给小动物起名,还要通过给小动物喂好吃的、和它一起玩 来让它保持好心情。需要倾听小动物的心声,来了解它现在是高兴还是生气。
面向对象基础知识
特性(attribute)与方法(method)
面向对象编程(Object-oriented programming, OOP)是一种编程思考方式,OOP中的基本构建单元式软件对象(software object),AKA 对象(object)。OOP允许用户将真实世界中的对象表示为软件对象,与真实世界中的对象一样,软件对象也有特征,比如真实世界中的小动物有名字,有颜色,有年龄,可以吃东西,可以跑,可以叫等等,这些特征表现在软件对象中,“名字”,“颜色”, “年龄”等这些名词就是软件对象的特性(attribute),“吃东西”,“跑”,“叫”这些行为就是方法(method)
在程序中对象(object)是如何被表示出来的呢?
对象 通过类(class)来创建(又称为实例化,instantiate)—— 用于定义对象属性和方法的代码都在类中编写。
类(class)好比是设计图,类不是对象,只是对象的设计图,可以根据同一个类实例化出各个对象(aka 实例, instance),这些实例拥有相同的结构。比如有一个Critter类,基于这个类我们就可以实例化出 cat, dog, panda等等小动物
面向对象编程
创建类、方法和对象
定义类
class Critter(object):
解释: class 是关键字, Critter是类名,约定俗成类名以大写字母开头
object 是一个最基本的内建类型,类可以基于object或其他任何已经定义好的类。
定义方法:
def talk(self):
print("Hi. I'm an instance of classs Critter.")
解释:实例方法的定义方式与之前介绍的函数定义方式相同。在任何实例方法中都必须有一个特殊的第一参数 -- self, 该参数使方法能够访问他所属的对象。
如果实例方法没有任何参数的话,调用的时候会报错。
实例化对象
crit = Critter()
调用实例方法
crit.talk()
__init__(self) 构造器方法(constructor method)
又称为初始化方法(initialization method),构造器方法通常用于设置对象的初始值,在新对象被创建后,构造器方法会被自动调用.
创建构造器
def __init__(self):
print("A new critter has been born!")
解释:__init__是Python内建的“特殊方法”,可以被Python识别,告诉Pyhone这是一个构造器方法。每当创建新的Critter对象进入其生命周期后,__init__()会被自动调用。
用于设置初始值
def __init__(self, name):
self.name = name
实例化对象如下:
crit1 = Critter("Poochie") ---> 创建对象是会自动调用__init__构造器,将传入的参数“Poochie”赋值给 自己的name变量
需要注意的是, 这里的参数需要与__init__中的一致,如果__init__(self),没有后面的name参数,使用 Critter("Poochie") 会提示错误
TypeError: Critter.__init__() takes 1 positional argument but 2 were given
self参数
self是所有方法的第一个参数,会自动接收调用该方法的对象的引用。也就是说,通过self,方法可以获取调用它的那个对象,于是就可以访问该对象的特性和方法了,也可以为该对象创建新特性。
在上面的例子中,def __init__(self, name): 参数self自动接收新Critter对象的引用,参数name则接收“Poochie”。“self.name = name”会为该对象创建出特性 name, 并将其设置为参数name的值。
Python有很多内建的“特殊方法”,都是以双下划线开始和结尾的。
__str__(self) 格式化输出格式
为自己的对象创建其字符串表示方式。当对象被打印时会显示这个字符串。程序自动调用。
def __str__(self):
rep = "Critter object\n)
rep += "name: " + self.name + "\n"
return rep
如果定义了__str__方法执行print(crit1)时,会打印如下
Critter object
name: Poochie
如果没有定义__str__方法执行print(crit1)时,打印如下
<__main__.Critter object at 0x00000248B01D0BF0>
# Attribute Critter # 演示创建和访问对象的特性 class Critter(object) : """A virtual pet""" # 定义构造器 def __init__(self, name) : print("A new critter has been born!") self.name = name # 定义字符串格式化方法 def __str__(self) : rep = "Critter object\n" rep += "name: " + self.name + "\n" return rep # 定义talk方法 def talk(self) : print("Hi. I'm", self.name, "\n") # 程序主题 crit1 = Critter("Poochie") crit1.talk() crit2 = Critter("Randolph") crit2.talk() print("Printing crit1: ") print(crit1) print("Printing crit1.name: ") print(crit1.name) input("\n\nPress the enter key to exit.") 类特性(class attribute)
上面有提到对象特性,相同类的不同对象可以拥有各自专属的值。比如十只小狗,拥有属于自己独特的名字。
对象特性与对象有关。
而类特性,与对象无关,但与整个类有关。比如 用户希望记录总共创建了多少动物,添加一个total特性,每实例化一个新对象,就更新total特性。
类特性是类本身的值。不管创建多少个对象类特性只有一份。
定义类特性
class Critter(object):
total = 0
访问类特性
Critter.total # 使用类名访问total
crit1.total # 使用对象访问total
可以通过类名和对象来访问类特性
对类特性赋值
Critter.total += 1 # 修改total值
crit1.total += 1 # 报错
只能通过类名访问的方式对类特性进行赋值和修改。
静态方法 @staticmethod
与类特性类似,属于类本身。通过类来调用,而非通过对象。
定义静态方法
class Critter(object):
total = 0
@staticmethod
def status():
print("\nThe total number of critters is", Critter.total)
解释:因为静态方法都是通过类来调用的,所以无需self参数。
修饰符@staticmethod 表示该方法为静态方法
调用静态方法
Critter.status()
# Classy Critter # 演示类特性和静态方法 class Critter(object) : """A virtual pet""" total = 0 @staticmethod def status(): print("\nThe total number of critters is", Critter.total) def __init__(self, name) : print("A critter has been born!") self.name = name Critter.total += 1 # 程序主题 print("Accessing the class attribute Critter.total:",) print(Critter.total) print("\nCreating critters.") crit1 = Critter("critter1") crit2 = Critter("critter2") crit3 = Critter("critter3") Critter.status(Critter) print("\nAccessing the class attribute through an object:",) print(crit1.total) input("\n\nPress the enter key to exit.") 对象封装
之前学习函数时,有接触到封装。回忆
函数是封装起来的,对程序主体部分中调用它的那些代码隐藏其内部工作细节。客户端与函数之间只会通过参数和返回值进行通信。
对象封装可以理解为客户端与对象之间之通过方法的参数和返回值进行通信。客户端代码应避免直接修改对象的特性值。
举个例子,假设有一个对象 Checking_Account对象,它有一个balance特性。现在需要处理取款业务(在某个对象的balance特性上减去某个数值)。要实现取款,客户端可以直接从balance上减去一个数,这种直接访问的方式很简单,但会导致一些问题,比如客户端可以把balance改为负数。
最好的方式是,写一个withdraw()的方法,然后让客户端通过向该方法传递取款金额的方式来请求一次取款操作。具体的工作又对象自己去处理。如果取款数额太大,对象可以对其进行处理,比如直接拒绝此次交易。这种通过方法对特性进行非直接访问的方式能够使类保持足够的安全性。
私有特性
默认情况下,对象的所有特性和方法都是公共的(public),它们可以被客户端直接访问或调用。为了强调封装,可以将特性和方法定义为私有的(private),只有该对象中的其他方法才能轻松访问或调用。
创建私有特性
class Critter(object) :
def __init__(self, name, mood) :
print("A nen critter has been born!")
self.name = name # 公共特性
self.__mood = mood # 私有特性
解释:在特性名称前加两个下划线,即可创建出私有特性
访问私有特性
在Critter对象内部访问私有特性,直接使用self.__mood
def talk(self) :
print("\nI'm", self.name)
prnt("Right now I feel", self.__mood, "\n")
虽说外部不能直接访问私有特性,但并非绝对,可以使用crit._Critter__mood访问到私有特性。
crit = Critter(name = "Poochie", mood = "happy")
print(crit.mood) #报错:AttributeError: 'Critter' object has no attribute 'mood'
print(crit.__mood) # 报错:AttributeError: 'Critter' object has no attribute '__mood'
print(crit._Critter__mood) # 一般来说外面是不能直接访问到私有特性的,但非绝对。
Python的私有性只是一种用于说明“特性或方法只应在对象内部使用”的指示器而已,也有助于防止不经意间访问到这样的特性或方法。尽量避免在类定义的外部直接访问对象 的私有特性或方法。
属性(@property)
如何访问对象的私有特性呢?Python提供了一些技术,比如属性(property), property可以精确控制特性的访问和修改方式
创建属性
创建属性就能控制对私有特性的读取访问,可以理解为java中的getter方法
class Critter(object) :
def __init__(self, name, mood) :
print("A nen critter has been born!")
self.name = name # 公共特性
self.__mood = mood # 私有特性
@property
def mood(self):
return self.__mood
对私有特性的写入访问,相当与java的setter方法
@mood.setter
def mood(self, new_mood):
if new_mood == "" :
print("A critter's mood can't be the empty string.")
else :
self.__mood = new_mood
print("Mood changes successful")
设置完属性后,在Critter类内部访问私有特性
# self.mood用于访问mood属性,也就是上面返回self.__mood的方法
print("Right now call property mood, I feel ", self.mood)
设置完属性后,在Critter类外部访问和修改私有特性
crit.mood = "grief" # 修改私有特性的值。这里是调用的@mood.setter注解修饰的方法
print(crit.mood) # 读取私有特性
私有方法
创建私有方法
def __private_method(self):
print("This is a private method.")
解释:在方法前加两个下划线,即可创建出私有方法
在Critter对象内部访问私有方法
def public_method(self):
print("This is a public method")
# 调用私有方法
self.__private_method()
在在Critter对象外部访问私有方法
crit = Critter(name = "Poochie", mood = "happy")
print(crit._Critter__mood)
crit.private_method() # AttributeError: 'Critter' object has no attribute 'private_method'
crit.__private_method() # AttributeError: 'Critter' object has no attribute '__private_method'
crit._Critter__private_method() # 可以通过这中方式访问,但最好不要。
尊重对象的隐私性,程序主体部分,要严格要求自己不去触碰对象的私有特性或方法。
在编写类时
- 要专门编写一些方法来避免出现“客户端需要直接访问对象特性”的情况;
- 对于那些只会在对象内部使用的特性和方法,应该将它们弄成私有的。
在使用对象时
- 尽量不要直接读取对象的特性;
- 避免直接修改对象的特性;
- 永远不要直接访问对象的私有特性或方法。
# Private Critter # 演示私有变量和方法 class Critter(object) : """A virtual pet""" # 构造器方法 def __init__(self, name, mood) : print("A nen critter has been born!") # 公共特性 self.name = name # 私有特性 self.__mood = mood # 私有特性的读取访问, 相当于java的getter方法 @property def mood(self): return self.__mood # 私有特性的写入访问,相当于java的setter方法 @mood.setter def mood(self, new_mood): if new_mood == "" : print("A critter's mood can't be the empty string.") else : self.__mood = new_mood print("Mood changes successful") # 行为方法 def talk(self) : print("\nI'm", self.name) # 直接访问私有特性mood print("Right now I feel", self.__mood, "\n") # self.mood用于访问mood属性,也就是上面返回self.__mood的方法 print("Right now call property mood, I feel ", self.mood) # 私有方法 def __private_method(self): print("This is a private method.") # 公有方法 def public_method(self): print("This is a public method") # 调用私有方法 self.__private_method() crit = Critter(name = "Poochie", mood = "happy") # print(crit._Critter__mood) # crit._Critter__private_method() crit.mood = "grief" print(crit.mood) Critter Caretaker程序
# Critter Caretaker # 一直需要细心呵护的虚拟宠物 class Critter(object) : """A virtual pet""" # 构造器方法,用于初始化特性 def __init__(self, name, hunger = 0, boredom = 0) : self.name = name self.hunger = hunger self.boredom = boredom # 私有方法,用于增加宠物的饥饿值和无聊值 def __pass_time(self) : self.hunger += 1 self.boredom += 1 # mood属性,根据饥饿值和无聊值实时计算得出 @property def mood(self) : unhappiness = self.hunger + self.boredom if unhappiness < 5 : m = "happy" elif 5 <= unhappiness <= 10 : m = "okay" elif 11 <= unhappiness <= 15 : m = "frustrated" else : m = "mad" return m # 用于宣告现在的心情 def talk(self) : print("I'm", self.name, "and I feel", self.mood, "now.\n") self.__pass_time() # eat方法根据food的值降低宠物的饥饿度,且动物的饥饿度一直被监测,不能低于0以下 # 入参food,默认值4. def eat(self, food = 4) : print("Brruppp. Thank you.") self.hunger -= food if self.hunger < 0 : self.hunger = 0 self.__pass_time() # play方法根据fun值降低动物的无聊程度,且动物无聊程度一直被监测,不能低于0以下 # 入参 fun, 默认值4 def play(self, fun = 4) : print("Wheee!") self.boredom -= fun if self.boredom < 0 : self.boredom = 0 self.__pass_time() # 主函数 def main() : crit_name = input("What do you want to name your critter?: ") crit = Critter(crit_name) # 创建菜单系统 choice = None while choice != "0" : print( """ Critter Caretaker 0 - Quit 1 - Listen to your critter 2 - Feed your critter 3 - Play with your critter """) choice = input("Choice: ") print() # 退出 if choice == "0" : prnt("Good-bye.") # 倾听宠物的心声 elif choice == "1" : crit.talk() # 给宠物喂食 elif choice == "2" : crit.eat() # 陪宠物玩耍 elif choice == "3" : crit.play() else : prnt("\nSorry, but", choice, "isn't a valid choice.") # 启动主程序 main() input("\n\nPress the enter key to exit.")