Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。
如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。
接下来我们先来简单的了解下面向对象的一些基本特征。
和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。
Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。对象可以包含任意数量和类型的数据。
面向对象的重要术语:
函数和面向对象编程的区别:
面向对象VS面向过程:
__init__ # 构造函数
Self.name = name # 实例变量、普通属性 或者叫 普通字段
public_object = "public" # 类变量、公有属性 或则叫 静态字段
self.__heart= "Normal" # 私有属性 在外面无法访问
def shot(self) # 类方法
def db_conn():
print("connecting db...")
def db_backup(dbname):
print("导出数据库...", dbname)
print("将备份文件打包,移至相应目录...")
def db_backup_test():
print("将备份文件导入测试库,看导入是否成功")
def main():
db_conn()
db_backup('my_db')
db_backup_test()
if __name__ == '__main__':
main()
# 运行结果:
# connecting db...
# 导出数据库... my_db
# 将备份文件打包,移至相应目录...
# 将备份文件导入测试库,看导入是否成功
class Role(object): #1、在定义类时继承object就是新式类,没有就是就是旧类式
public_object = "public" #2、在类例定义一个公有属性:所有实例都能访问的属性叫“公有属性”
def __init__(self,name,role,weapon,life_value=100,money=15000): #构造函数==初始化方法:模板
self.name = name #3、普通属性
self.__heart= "Normal" #4、私有属性在外面无法访问
def shot(self): #5、类的方法
print("%s is shooting..."%self.name)
class Role(object): #在定义类时继承object就是新式类,没有就是就是旧类式
public_object = "public" #在类例定义一个公有属性:所有实例都能访问的属性叫“公有属性”
def __init__(self,name,role,weapon,life_value=100,money=15000): #构造函数==初始化方法:模板
self.name = name #普通属性
self.role = role
self.weapon = weapon
self.life_value = life_value
self.money = money
self.__heart= "Normal" #私有属性在外面无法访问
def shot(self): #类的方法
print("%s is shooting..."%self.name)
def got_shot(self):
print("ah...,I got shot...")
def buy_gun(self,gun_name):
print("%s just bought %s" %(self.name,gun_name))
self.weapon = gun_name #在购买后让实例值改变
#在下面实例化其实就是传入: Role('r1','Alex','police','AK47') 把r1传给了self
#r1就是实例化后产生的当前Role类的实例,所以self就是实例本身
#我理解r1其实就是__init__函数的内存地址
#所以在下面函数中调用self.name就相当于调用r1.name所以可以调用
r1 = Role('Alex','police','AK47') #生成一个角色 只要一实例化就会自动调用__init__
r2 = Role('Jack','terrorist','B22') #生成一个角色
r1.shot()
print(r2.weapon) #在调用r2.buy_gun('AK47')前是:B22
r2.buy_gun('AK47')
print(r2.weapon) #在调用r2.buy_gun('AK47')后是:AK47
#私有属性
# print(r2.__heart) #这里的.__heart是私有属性,所以在外部无法访问
print(r2._Role__heart) #强制访问私有属性的方法
#公有属性
print(r2.public_object) #打印出类的公有属性
Role.public_object = 'change_public' #从全局改变类的公有属性 r1,r2的公有属性都会变
print(r2.public_object) #这里打印可以看出公有属性变成类“change_public"
r2.public_object = "public_r2" #改变r2对象的公有属性,r1不会变
print(r2.public_object) #打印出改变后的r2公有属性
公有属性,普通属性,私有属性 比较
类中函数私有化
class Secretive:
def __accessible(self):
print("you can't see me,unless you're calling internally")
def accessible(self):
print("The secret message is:")
self.__accessible()
s = Secretive()
s.accessible()
# 运行结果:
# The secret message is:
# you can't see me,unless you're calling internally
Encapsulation 封装(隐藏实现细节)
class Foo:
def __init__(self, name, age ,gender):
self.name = name
self.age = age
self.gender = gender
def eat(self):
print "%s,%s岁,%s,吃奶" %(self.name, self.age, self.gender)
def he(self):
print "%s,%s岁,%s,喝水" %(self.name, self.age, self.gender)
def shui(self):
print "%s,%s岁,%s,睡觉" %(self.name, self.age, self.gender)
a = Foo('jack', 10, '男')
a.eat()
a.he()
a.shui()
b = Foo('rose', 11, '女')
b.eat()
b.he()
b.shui()
Inheritance 继承(代码重用)
class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '猫'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 执行 #########
c1 = Cat('猫one')
c1.eat()
c2 = Cat('猫two')
c2.drink()
d1 = Dog('狗one')
d1.eat()
class Person(object):
def __init__(self,name,age): #执行Person.__init__(self,name,age)时就会将传入的参数执行一遍
self.name = name #所以在BlackPerson中不仅有name,age而且还有sex
self.age = age
self.sex = "normal"
def talk(self):
print("person is talking....")
class WhitePerson(Person):
pass
class BlackPerson(Person):
def __init__(self,name,age,strength): #先覆盖,再继承,再重构
#先覆盖父类的__init__方法,再继承父类__init__,再加自己的参数
Person.__init__(self,name,age) #先继承父类Person,这里self就是BlackPerson本身
#先将name,age传给子类BlackPerson,然后调用Person.__init__构造方法将参数出入父类()
self.strength = strength #然后再重构自己的方法,即写自己的参数
print(self.name,self.age,self.sex)
print(self.strength)
def talk(self):
print("black balabla")
def walk(self):
print("is walking....")
b = BlackPerson("wei er smith",22,"Strong")
b.talk()
b.walk()
# 运行结果:
# wei er smith 22 normal
# Strong
# black balabla
# is walking....
# person is talking....
注意: 关于多继承
Polymorphism 多态(接口重用)
class Animal:
def __init__(self, name): # Constructor of the class
self.name = name
def talk(self): # Abstract method, defined by convention only
raise NotImplementedError("Subclass must implement abstract method")
class Cat(Animal):
def talk(self):
return 'Meow!'
class Dog(Animal):
def talk(self):
return 'Woof! Woof!'
animals = [Cat('Missy'),
Dog('Lassie')]
for animal in animals:
print(animal.name + ': ' + animal.talk())
# 运行结果:
# Missy: Meow!
# Lassie: Woof! Woof!
静态方法(用这个装饰器来表示 @staticmethod)
错误示例:
class Person(object):
def __init__(self, name):
self.name = name
@staticmethod # 把eat方法变为静态方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
##############
结果:
TypeError: eat() missing 1 required positional argument: 'self'
因为用静态方法把eat这个方法与Person这个类截断了,eat方法就没有了类的属性了,所以获取不到self.name这个变量。
正确示例:
class Person(object):
def __init__(self, name):
self.name = name
@staticmethod # 把eat方法变为静态方法
def eat(x):
print("%s is eating" % x)
d = Person("xiaoming")
d.eat("jack")
#就把eat方法当作一个独立的函数给他传参就行了
类方法(用这个装饰器来表示 @classmethod)
错误示例:
class Person(object):
def __init__(self, name):
self.name = name
@classmethod # 把eat方法变为类方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
###########
结果:
AttributeError: type object 'Person' has no attribute 'name'
因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量
class Person(object):
name="杰克"
def __init__(self, name):
self.name = name
@classmethod # 把eat方法变为类方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
属性方法(用这个装饰器表示 @property)
错误示例:
class Person(object):
def __init__(self, name):
self.name = name
@property # 把eat方法变为属性方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat()
##########
结果:
TypeError: 'NoneType' object is not callable
因为eat此时已经变成一个属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
class Person(object):
def __init__(self, name):
self.name = name
@property # 把eat方法变为属性方法
def eat(self):
print("%s is eating" % self.name)
d = Person("xiaoming")
d.eat
class Flight(object):
def __init__(self,name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 1
@property
def flight_status(self):
status = self.checking_status()
if status == 0 :
print("flight got canceled...")
elif status == 1 :
print("flight is arrived...")
elif status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
f = Flight("CA980")
f.flight_status
成员修饰符
class a: # 说明父类的私有成员无法在子类中继承
def __init__(self):
self.ge=123
self.__gene=456
class b(a):
def __init__(self,name):
self.name=name
self.__age=18
super(b,self).__init__() # 这一行会报错
def show(self):
print(self.name)
print(self.__age)
print(self.ge)
print(self.__gene) # 这一行也会报错
obj=b("xiaoming")
print(obj.name)
print(obj.ge)
# print(obj.__gene) # 这个也会报错
obj.show()
上面就是类里面的私有成员了。
class Dog(object):
def __init__(self,name):
self.name = '实例变量'
self.name = name
def __call__(self, *args, **kwargs):
print("running call")
print(args,kwargs)
d = Dog("ChenRonghua")
d(name='tom') # 如果只实例化,而不d()调用,__call__函数不会执行
# 运行结果:
# running call
# () {'name': 'tom'}
class Foo:
def __str__(self):
return 'alex li'
obj = Foo()
print(obj) # 输出:alex li
class abc:
def __init__(self,age):
self.age=age
def __add__(self,obj):
return self.age+obj.age
a1=abc(18)
a2=abc(20)
print(a1+a2)
#执行结果:38
class Province:
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的成员,即:静态字段、方法、
print(Province.__dict__)
# 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}
obj1 = Province('HeBei',10000)
print(obj1.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'}
obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 3888, 'name': 'HeNan'}
class Foo:
""" 输出类的描述类信息 """
def func(self):
pass
print(Foo.__doc__) #运行结果:描输出类的描述类信息
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # __getitem__ k1
obj['k2'] = 'wupeiqi' # __setitem__ k2 wupeiqi
del obj['k1'] # __delitem__ k1
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
self.li=[1,2,3,4,5,6,7]
def __getitem__(self, item): # 匹配:对象[item]这种形式
if isinstance(item,slice): # 如果是slice对象,返回切片后的结果
return self.li[item] # 返回切片结果
elif isinstance(item,int): # 如果是整形,说明是索引
return item+10
def __setitem__(self, key, value): # 匹配:对象[key]=value这种形式
print(key,value)
def __delitem__(self, key): # 匹配:del 对象[key]这种形式
print(key)
def __getslice__(self,index1,index2):
print(index1,index2)
li=Foo("alex",18)
print(li[3:5])
#执行结果:
[4, 5]
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def __iter__(self):
return iter([1,2,3,4,5]) # 返回的是一个迭代器
li=Foo("alex",18)
# 1.如果类中有__iter__方法,他的对象就是可迭代对象
# 2.对象.__iter()的返回值是一个迭代器
# 3.for循环的如果是迭代器,直接执行.next方法
# 4.for循环的如果是可迭代对象,先执行对象.__iter(),获取迭代器再执行next
for i in li:
print(i)
#执行结果:
1
2
3
4
5
class User(object):
def __init__(self,name,age):
print('__init__')
def __new__(cls, *args, **kwargs):
print("__new__",args,kwargs)
return object.__new__(cls) # 调用一下object的__new__方法否则不往下走
d = User('tom',100)
# 运行结果:
# __new__ ('tom', 100) {}
# __init__
__new__和__init__的区别
isinstance和issubclass
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
class Son(Foo):
pass
obj=Son("xiaoming",18)
print(isinstance(obj,Foo))
执行结果:True
issubclass用来判断一个类是否是某个类的子类,返回的是一个bool类型数据,代码如下:
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
class Son(Foo):
pass
obj=Son("xiaoming",18)
print(issubclass(Son,Foo))
执行结果:True
class A: #经典类写法
pass
class B(object): #新式类写法
pass
SchoolMember.__init__(self,name,age,sex) #经典类写法
super(Teacher,self).__init__(name,age,sex) #新式类写法
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry == True:
print("Aaaah...")
self.hungry = False
else:
print("no, thanks")
a = Bird()
a.eat()
a.eat()
class SongBird(Bird):
def __init__(self):
# Bird.__init__(self) # 经典类写法
# super(SongBird,self).__init__() # 新式类写法
self.sound = 'Squawk'
def sing(self):
print(self.sound)
b = SongBird()
b.sing()
b.eat()
class SchoolMember(object):
'''学校成员基类'''
member = 0
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
self.enroll() #只要一实例化就自动注册
def enroll(self):
'''注册'''
print("just enrolled a new school member [%s]"%self.name)
SchoolMember.member += 1
def tell(self):
print("------info:%s-------"%self.name)
for k,v in self.__dict__.items():
print("\t",k,':',v)
def __del__(self): #'''析构方法'''
print("开除了[%s]...."%self.name)
SchoolMember.member -= 1
class Teacher(SchoolMember):
def __init__(self,name,age,sex,salary,course):
SchoolMember.__init__(self,name,age,sex) #经典类写法
#super(Teacher,self).__init__(name,age,sex) #新式类写法
self.salary = salary
self.cource = course
# self.enroll()
def teaching(self):
print("Teacher [%s] is teachin g [%s]"%(self.name,self.cource))
class Student(SchoolMember):
def __init__(self,name,age,sex,course,tuition):
SchoolMember.__init__(self,name,age,sex)
self.cource = course
self.tuition = tuition
self.amount = 0
# self.enroll()
def pay_tuition(self,amount):
print("student [%s] has just paied [%s]"%(self.name,amount))
self.amount += amount
t1 = Teacher("Wusir",28,"F*M",3000,"python")
s1 = Student("HaiTao",38,"M","PYS15",300000)
s2 = Student("Lichuang",12,"M","PYS15",11000)
print(SchoolMember.member)
del s2
print(SchoolMember.member)
t1.tell()
class SchoolMember(object):
def __init__(self,name,age):
self.name = name
self.age = age
def tell(self):
print("I am SchoolMember,my name is %s"%self.name)
SM1 = SchoolMember("tom",100)
class Teacher(object):
def __init__(self,sex,t1):
self.SchoolMember = t1
def user_SM(self):
self.SchoolMember.tell()
T1 = Teacher('F',SM1)
T1.user_SM()
class Dog(object):
def __init__(self,name,dog_type):
self.name = name
self.type = dog_type
def sayhi(self):
print("i am a dog,my name is %s"%self.name)
d = Dog('LiChuang','京巴') #等价于 Dog(d,"LiChuang","京巴")
d.sayhi() #运行结果:i am a dog,my name is LiChuang
class Dog(object):
def eat(self,food):
print("eat method!!!")
d = Dog()
#hasattr判断对象d是否有eat方法,有返回True,没有返回False
print(hasattr(d,'eat')) #True
print(hasattr(d,'cat')) #False
class Dog(object):
def eat(self):
print("eat method!!!")
d = Dog()
if hasattr(d,'eat'): # hasattr判断实例是否有eat方法
func = getattr(d, 'eat') # getattr获取实例d的eat方法内存地址
func() # 执行实例d的eat方法
#运行结果: eat method!!!
class Dog(object):
def eat(self,food):
print("eat method!!!")
d = Dog()
def bulk(self): #给Dog类添加一个方法必须先写这个方法
print('bulk method add to Dog obj')
d = Dog()
setattr(d,"bulk",bulk) #将bulk方法添加到实例d中,命名为talk方法
d.bulk(d) #实例d调用刚刚添加的talk方法时必须将实例d自身当变量传入,因为他不会自己传入self
#1. 注:setattr(x,’y’,z)用法: x就是实例对象 y就是在实例中调用时用的名字,z就是改变属性的值/或则要添加的函数的名字(正真的函数)
#2. setattr( 具体实例名称 , ’在类中调用时使用的名称’ , 要添加的真实函数的名称)
#3. 作用: setattr(d,"bulk",bulk) 将bulk方法添加到实例d中并且在实例中以bulk名称调用
class Dog(object):
def __init__(self,name):
self.name = name
def eat(self,food):
print("eat method!!!")
d = Dog('Fly')
#1 实例d中没有sex这个属性,就会动态添加一个属性 sex = Male
setattr(d,"sex",'Male') #给实例d添加一个属性:sex=Male
print("将实例d添加一个新属性sex=Male:\t",d.sex)
#2 如果实例d中本身存在这个属性那么 新的值就会覆盖这个属性
setattr(d,'name','name change to jack')
print("原本名字是Fly改变后的名字是:\t",d.name)
# 运行结果:
# 将实例d添加一个新属性sex=Male: Male
# 原本名字是Fly改变后的名字是: name change to jack
class Dog(object):
def __init__(self,name):
self.name = name
def eat(self,food):
print("%s is eating....."%self.name)
d = Dog("NiuHanYang")
choice = input(">>:").strip()
if hasattr(d,choice):
delattr(d,choice) #使用delattr(d,choice)删除实例的属性那么所以下面打印就会报错
print(d.name)
# 运行结果:
# >>:name #输入的值是name
# 下面是报错信息
# Traceback (most recent call last):
# File "C:/Users/admin/PycharmProjects/s14/Day7/test1.py", line 10, in <module>
单例模式原理及作用
创建单例模式举例
class Foo(object):
instance = None
def __init__(self):
self.name = 'alex'
def __new__(cls, *args, **kwargs):
if Foo.instance:
return Foo.instance
else:
Foo.instance = object.__new__(cls,*args,**kwargs)
return Foo.instance
obj1 = Foo() # obj1和obj2获取的就是__new__方法返回的内容
obj2 = Foo()
print(obj1,obj2) # 运行结果: <__main__.Foo object at 0x00D3B450> <__main__.Foo object at 0x00D3B450>
# 运行结果说明:
# 这可以看到我们新建的两个Foo()对象内存地址相同,说明使用的•同一个类,没有重复建立类
这里介绍一个设计模式,设计模式在程序员写了两三年代码的时候,到一定境界了,才会考虑到设计模式对于程序带来的好处,从而使用各种设计模式,这里只是简单的介绍一个简单的设计模式:单例模式。在面向对象中的单例模式就是一个类只有一个对象,所有的操作都通过这个对象来完成,这就是面向对象中的单例模式,下面是实现代码:
class Foo: # 单例模式
__v=None
@classmethod
def ge_instance(cls):
if cls.__v:
return cls.__v
else:
cls.__v=Foo()
return cls.__v
obj1=Foo.ge_instance()
print(obj1)
obj2=Foo.ge_instance()
print(obj2)
obj3=Foo.ge_instance()
print(obj3)
执行结果:
<__main__.Foo object at 0x000001D2ABA01860>
<__main__.Foo object at 0x000001D2ABA01860>
<__main__.Foo object at 0x000001D2ABA01860>
可以看到,三个对象的内存地址都是一样的,其实,这三个变量中存储的都是同一个对象的内存地址,这样有什么好处呢?能够节省资源,就比如在数据库连接池的时候就可以使用单例模式,只创建一个类的对象供其他程序调用,还有在web服务中接收请求也可以使用单例模式来实现,节省资源。