目录
1.12.7 __getitem__、__setitem__、__delitem__
1.12.8 __getslice__、__setslice__、__delslice__
2、读一个文件,包含英文句子,请统计共多少个不重复的单词,并在另外一个文件中内容每个单词及他的出现次数
3、将字典的key和value互换,生成新的字典,不考虑value重复的情况
4、网游:写个主程序,打架,随机生成多个人物,放到列表中。使用随机的方法,让其中的人物随机打架,最后打印一下所有人的血值和经验值
9、两个3*3的矩阵,实现其对应位置的数据相加,并返回一个矩阵
一、面向对象编程
1.1 概念
在编程领域有三种实现方式,面向过程,面向函数和面向对象,区别如下:
- 面向过程:按照业务逻辑一步步实现响应的需求
- 面向函数:将常用的功能封装到函数,避免重复编写代码,而且便于维护
- 面向对象:将函数进行分类和封装,可以更快更好的开发
- 类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 实例化:创建一个类的实例,类的具体对象。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
- 数据成员:类变量或者实例变量,用于处理类及其实例对象的相关的数据。
- 类变量指的是在类中,但在各个类方法外定义的变量。所有类的实例化对象都同时共享类变量,类变量的调用方式有2种:1)使用类名直接调用;2)使用类的实例化对象调用
- 实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。
- 局部变量:直接以“变量名=值”的方式进行定义。局部变量只能用于所在类方法中,函数执行完成后,局部变量也会被销毁。
- 方法:类中定义的函数。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
1.2 函数式编程和面向对象编程的选择
python同时支持函数式编程和面向对象编程,不像java是完全面向对象。那么函数式编程和面向对象编程该如何选择呢?
比较:
- 函数式编程直接执行函数即可,而面向对象编程还需要创建对象,通过对象执行方法,函数式编程貌似更简单一些;
- 函数执行完毕后无法保存数据的状态,数据管理不方便。而面向对象编程可以将对象序列化,即使关闭程序后反序列话依然可以取出来。
- 函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)
结论:
➢函数式的应用场景 --> 各个函数之间是独立且无共用的数据
➢面向对象编程的应用场景 -->各个函数公用一组数据
所以,一般在Python开发中,全部使用面向对象或面向对象和函数式混合使用
1. 多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
2. 需要创建多个事物,每个事物属性个数相同,但是值的需求不同。如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
1.3 创建类
使用class语句来创建一个新类,class之后为类的名称(类名首字母大写,采用驼峰命名法),类名后有一括号,括号中为基类(可以有多个基类)然后以冒号结尾。
class ClassName(BaseClass1,BaseClass2,…):
'optional class documentation string'#类的帮助信息
class_suite #类体
以下3中方式都可以创建类:
class Person():pass
class Person:pass
class Person(object):pass
类的帮助信息可以通过ClassName.__doc__查看。
class_suite 类体由类的成员、方法、数据属性组成。
#coding=utf-8
class Employee(object):
#所有员工基类
empCount = 0
def __init__(self, name, salary) : #类的构造函数
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self) : #实例方法
print ("total employee ",Employee.empCount)
def displayEmployee(self) :
print ("name :",self.name , ", salary :", self.salary)
emp1 = Employee("wulaoshi",100)
emp2 = Employee("lilaoshi",500)
emp1.displayEmployee()
emp2.displayEmployee()
print ("Total Employee %d" % Employee.empCount)
说明:
- empCount变量是一个类变量(也叫静态变量),它的值将在这个类的所有实例之间共享,可以在内部类或外部类使用
- 类通过方法(在class语句内由def语句编写的)为实例提供行为。因为这种嵌套的def会在类中对变量名进行赋值,实际效果就是把属性添加到类对象之中,从而可以由所有实例和子类继承。def语句块出现在类内部时,通常就叫方法,而且会自动接收第一个特殊参数(通常为self),这个参数代表了被处理的实例。
- 类中第一个方法__init__()是类的构造方法,创建类的实例会调用此方法,如果没有显式的定义此方法时,默认会定义一个空的构造方法
- 第一个参数为self的方法代表的是实例方法,self表示的就是实例本身。
- 所有类最底层的基类都是object,不写的话默认继承的就是object
1.4 创建实例对象
要创建一个类的实例,可以使用类的名称,并通过__init__()方法来接受参数。__init__()方法是python自动调用的
class Person: #父类
Count = 0
def __init__(self,name):
self.name = name
print(id(self.name))
def get_name(self):#self:传递的参数是实例的内存地址,告诉这个方法,使用哪个实例的数据
"""获取名字"""
return self.name
def set_name(self,value):
self.name = value
class Teacher(Person): #子类
"""此类主要用于管理老师的数据"""
def __init__(self,name,salary):
Person.__init__(self,name)
print(id(self.name))
# self.name = name
self.salary = salary
Teacher.Count += 1
self.Count = 100
def get_name(self):#override:重写父类方法
return "光荣之路" + self.name
t = Teacher("吴老师",1000)
print(t.get_name())
s = Teacher("李老师",2000)
print(s.get_name())
print(Teacher.__doc__)
print(Person.get_name.__doc__)#打印:获取名字
print(t.get_name.__doc__) #打印:None
print(Teacher.Count) # 打印:2
print(t.Count) #打印:100
1.5 访问类的实例方法或属性
访问对象的属性及方法,需要使用(.)来访问。如果访问类变量,既可以直接使用类名称访问,也可以用类的实例访问。
注意:在实例化类时,不需要再显示写self参数了,会默认带入,表示指向实例本身。
class P:
def __init__(self,a):
self.a = a
def print_str(self):
print("P")
def func(self): #访问类内的实例方法
self.print_str()
p = P(2)#实例化
print(p.a)#访问实例属性
p.func()#访问实例方法
1.6 类和对象在内存中是如何保存
类及类中的方法和类属性在内存中只有一份,而每个对象在内存中都有一份。
根据类创建对象时,对象中除了保存 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过obj1执行【方法一】时过程如下:根据当前对象中的类对象指针找到类中的方法将对象obj1当作参数传给方法的第一个参数 self
1.7 封装和调用
1.7.1 封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
class Foo:
def __init__(self, name, age):
self.name=name
self.age=age
#根据类Foo创建对象
#自动执行Foo类的__init__方法
obj1=Foo('wu',18) #将wu和18分别封装到obj1/self的name和age属性中
print (obj1.name)# 直接调用obj1对象的name属性
print (obj1.age)# 直接调用obj1对象的age属性
obj2=Foo('alex',73)
print (obj2.name)# 直接调用obj2对象的name属性
print (obj2.age) # 直接调用obj2对象的age属性
说明:
- self 是一个形式参数,当执行 obj1 = Foo('wui', 18 ) 时,self 等于 obj1;
- 当执行 obj2 = Foo('alex',73) 时,self 等于 obj2
- 内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有name和age属性
class Animal(object):
def __init__(self, name): #构造方法一个对象创建后会立即调用此方法
self.Name = name
print(self.Name)
def accessibleMethod(self):
#绑定方法对外公开
print ("I have a self! current name is:")
print (self.Name)
print ("the secret message is:")
self.__inaccessible()
def __inaccessible(self):
#私有方法对外不公开以双下划线开头
print ("U cannot see me...")
@staticmethod
def staticMethod():
# self.accessibleMethod() #在静态方法中无法 #直接调用实例方法直接抛出NameError异常
print ("this is a static method")
def setName(self,name):
#访问器函数
print("setName:")
self.Name=name
def getName(self):
#访问器函数
print("getName:")
return self.Name
a = Animal("learns")#打印:learns
print(a.getName)#打印:<bound method Animal.getName of <__main__.Animal object at 0x000001AF06927A60>>
a.setName("sr")#打印:setName:
print("new name:",a.getName())
"""打印
getName:
new name:learns
"""
a.staticMethod()#打印:this is a static method
a.accessibleMethod()
"""
打印
I have a self! current name is:
sr
the secret message is:
U cannot see me...
"""
1.7.2 调用
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过 self 间接调用
1、通过对象直接调用被封装的内容
上述代码展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print(self.name) #方法2的调用
print(self.age) #方法2的调用
obj1 = Foo('wulaoshi', 18)
obj1.detail() # Python默认会将obj1传给self参数,即:
#obj1.detail(obj1)
#所以,此时方法内部的 self = obj1,即:self.name 是 #wulaoshi ;self.age 是 18
obj2 = Foo('alex', 73)
obj2.detail() # Python默认会将obj2传给self参数,即: obj2.detail(obj2)
#所以,此时方法内部的 self = obj2,即:self.name 是 #alex ; self.age是 78
1.8 类成员
类的成员可以分为三大类:字段、方法和属性
1.8.1 字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同
- 普通字段,又叫实例变量:属于对象,在每个对象中都要保存一份,类中由普通方法调用,类外由对象调用,普通方法调用方式:self.普通字段;对象调用方式:对象名.普通字段
- 静态字段,又叫类变量、静态成员变量:属于类,在内存中只保存一份,类变量在所有实例化的对象中是公用(共享)的。类中所有方法都可调用,类外类和对象都可调用,类中普通方法调用方式:类名.静态字段;类中类方法调用方式:类名.静态字段或cls.静态字段;类外对象调用方式:对象名.静态字段
类变量紧接在类名后面定义,相当于java和c++的static变量。
实例变量在__init__()函数里定义,相当于java和c++的普通变量。
class P:
def __init__(self,name):
self.name=name
def print_info(self): #普通方法(实例方法)
print("info method is inworked!")
self.age=23
print(self.age)
a=P("wulaoshi")
a.print_info()
print(a.age)
b=P("Amy")
print(b.name)
print(b.age)#报AttributeError: 'P' object has no attribute 'age'
实例变量和类变量重名时,使用实例访问这个变量的时候:优先访问实例变量,del删除之后访问的就是类变量。
>>> class Employee(object):
... #所有员工基类
... empCount = 0
...
... def __init__(self,name,salary):
... #类的构造函数
... self.name = name
... self.salary = salary
... Employee.empCount += 1
...
...
>>> a=Employee("AAA",1000)
>>> a.name
'AAA'
>>> a.empCount
1
>>> a.empCount=3
>>> a.empCount
3
>>> Employee.empCount
1
>>> del a.empCount
>>> a.empCount
1
class f(object):
count = 12#类变量
t = f()
print("t.count内存地址 =", id(t.count), "t.count =", t.count)# t.count = 12
print("f.count内存地址 =", id(f.count), "f.count =", f.count)# f.count = 12,id(f.count)与id(t.count)相同
t.count = 10
print("通过实例对象修改count的值后")
print("t.count内存地址 =", id(t.count), "t.count =", t.count)
print("f.count内存地址 =", id(f.count), "f.count =", f.count)# f.count = 12,id(f.count)与id(t.count)不同
局部变量
在类的实例方法中定义的变量,叫局部变量,其作用域仅限于其所在的函数内部,执行到该函数,局部变量生成,函数退出后,局部变量被消亡。
class Person(object):
id = 12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name = name #类的实例变量
self.inName = 'ads' #类的实例变量
def getName(self) :
name = "lucy" #局部变量
return self.name, name
p = Person('linda')
print (p.name) #访问对象的实例变量 #通过函数访问实例变量和局部变量
res = p.getName()
print (res)