文章目录
一、方法的重载
python中没有方法的重载。在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含3个部分:方法名、参数数量、参数类型。而在python中,如果定义了多个同名方法,那么只有最后一个有效,即方法可以重载。
#python中没一个方法的重载。定义多个同名方法,只有最后一个有效
class person:
def say_hi(self):
print("hello")
def say_hi(self,name):
print("{0},hello".format(name))
p1=person()
p1.say_hi() #不带参数,将会报错
二、方法的动态性
python是动态语言,我们可以动态的为类添加新的方法,或者动态的修改类已有的方法。具体例子如下:
class Person:
def work(self):
print("努力!")
def play_game(s):
print("{0}在玩游戏".format(s))
Person.play=play_game #为类添加新方法
p=Person()
p.work()
p.play()
三、私有属性
私有属性和私有方法,能够实现对象的封装,让外人不可随意查看。
就像电脑一样,只需要会用,不需要知道内部的工作原理。
- 注:方法本质上也是属性,只不过是通过()执行而已。
- 要点:
①两个下划线开头的属性是私有属性:__私有属性名;其他的命名方法为公共属性;
②类内部可以直接访问私有属性(方法):类名.__私有属性名
③类外部不能直接访问私有属性(方法),但可以通过“对象名._类名__私有属性(方法)名”访问私有属性(方法)
class Employee:
__company='百战' #类的私有属性
def __init__(self,name,age):
self.name=name
self.__age=age #实例的私有属性
def __work(self): #私有方法
print("{0} ,please work hard!".format(self.__age)) #在类内部,可以随意调用私有属性
print(Employee.__company) #类的私有属性的调用方法
e=Employee("何旯",18)
print(e._Employee__age) #私有属性的调用方法
e._Employee__work() #私有方法的调用方法
print(e._Employee__company) #打印类的私有属性
print(dir(e)) #打印类可以调用的所有属性和方法
四、@property装饰器
(一)使用装饰器调用方法
- 含义:@property可以将一个方法的调用方式变成“属性调用”
- 使用方法:在方法的前一行,写上@property
- 优点:调用方法时,不需要在后面加()了,像调用属性一样:对象名.方法名
class Empolyee:
def salary(self):
return 10000
#传统调用方式
emp1=Employee() #创建对象
print(emp1.salary()) #将会打印10000
#@property调用
class Empolyee:
@property
def salary(self):
return 10000
emp1=Employee() #创建对象
print(emp1.salary) #像调用属性一样,调用方法,但emp1.salary无法更改
(二)使用get和set修改属性
#使用get和set方法修改属性
class Employee:
def __init__(self,name,salary):
self.__name=name #私有属性
self.__salary=salary
def get_salary(self):
return self.__salary
def set_salary(self,salary):
if 10000<salary<50000:
self.__salary=salary #修改私有属性
else:
print("输入错误!")
emp1=Employee('高',30000)
print(emp1.get_salary()) #打印30000
emp1.set_salary(-20000)
print(emp1.get_salary()) #打印"录入错误!"
emp1.set_salary(40000)
print(emp1.get_salary()) #打印40000
(三)使用@property和set修改属性
#使用@property和set方法修改属性
class Employee:
def __init__(self,name,salary):
self.__name=name
self.__salary=salary
@property
def salary(self): #对原来的get方法进行了简化
return self.__salary
def set_salary(self,salary):
if 10000<salary<50000:
self.__salary=salary
else:
print("输入错误!")
emp1=Employee('高',30000)
print(emp1.salary) #使用装饰器后,调用方法时不需要加括号
emp1.set_salary(-20000)
print(emp1.salary) #打印"录入错误!"
emp1.set_salary(40000)
print(emp1.salary) #打印40000
#可以看到使用@property装饰器,可以将get方法简化,可以直接将方法当作属性使用
五、面向对象的三大特征说明(封装、继承和多态)
python是面向对象语言。面向对象语言都有三大特性:封装(隐藏)、继承和多态。
- 封装(隐藏)
- 含义:隐藏对象的属性和实现细节,只对外暴露“相关调用方法”。
- 实现方式:私有属性、私有方法
- 继承
- 继承可以让子类具有父类的特性,提高了代码的重用性
- 原有父类设计不变的情况下,子类可以增加新的功能,或者改进已有算法
- 多态
- 含义:由于对象不同,同一个方法的调用会产生不同的行为
- 例子:中国人用筷子吃饭,美国人用刀叉吃饭等
(一)继承
已有的类:父类
新的类:子类(派生类)
1. 成员继承
- 语法格式:class 子类类名(父类1[,父类2,…]):类体
- 如果没有指定父类,则默认父类为object类。object类中定义了一些所有类共有的默认实现,比如__new__()
- 定义子类时,必须在其构造函数中调用父类的构造函数。
#定义父类
class Person:
def __init__(self,name,age):
self.name=name
self.__age=age
def say_age(self):
print("不知道")
#定义子类
class Student(Person):
def __init__(self,name,age,score):
Person.__init__(self,name,age) #继承。这里不调用也不出错,但是如果不显式地调用父类初始化方法,解释器本身也不会去调用,就无法使用name,age
Student.score =score
#Student->Person->object
stu1=Student("高",18,80)
print(stu1.name) #将会打印出“高”
print(stu1.age) #不会打印,因为age是私有属性
print(stu1.__Person__age) #通过print(dir(stu1)),可以看到打印age的方法。此处将会打印出18
stu1.say_age() #将会打印出不知道
2.方法的重写
#定义父类
class Person:
def __init__(self,name,age):
self.name=name
self.__age=age
def say_age(self):
print("我的年龄为:",self.__age)
def say_introduce(self):
print("我的名字为:",self.name)
#定义子类
class Student(Person):
def __init__(self,name,age,score):
Person.__init__(self,name,age)
Student.score =score
stu1=Student("高",18,80)
stu1.say_age() #子类继承了父类的方法
stu1.say_introduce()
#重写父类的方法
class Student(Person):
def __init__(self,name,age,score):
Person.__init__(self,name,age)
Student.score =score
def say_introduce(self): #由于python有方法的重载特性,直接在子类中定义一个重名方法,python将会调用子类的方法
print("报告老师,我的名字是:{0}".format(self.name))
stu1.say_introduce() #这时程序将会调用子类的方法
3.object根类
- object根类:object根类是所有类的父类。学习object类的结构,对深入学习python有好处。
- 使用dir()可以查看类的属性
4.重写__str__()方法
- object有一个__str__()方法,用于返回对于“对象的描述”
- str()方法需要搭配print
- str()方法可以重写
class Student:
def __init__(self,name):
self.name=name
p=Student("gao")
print(p) #这里会打印出关于对象p的地址
#测试重写object的__str__()方法
class Student:
def __init__(self,name):
self.name=name
def __str__(self):
return "名字是{0}".format(self.name)
p=Student("gao")
print(p) #使用str()方法改写后,这里会打印出“名字是gao”
5.多重继承
python支持多重继承,一个子类可以有多个父类,但是这样会被“类的整体层次”搞得异常复杂,应尽量避免使用。
class A:
def aa(self):
print("aa")
class B:
def bb(self):
print("bb")
class C(A,B):
def cc(self):
print("cc")
c=C()
c.cc() #将会打印出cc
c.aa() #将会打印出aa
c.bb() #将会打印出bb
6.mro()
-
mro():通过类的方法mro()或者类的属性__mro__可以输出类的继承结构,或者说层次结构。
-
例子如下:
class A:pass
class B(A):pass
class C(B):pass
print(C.miro())
输出结果为:
- 由于python支持多重继承,所以父类中如果有相同名字的方法,在子类没有指定父类名时,会按照“从左到右”的顺序搜索。
class A:
def aa(self):
print("aa")
def say(self):
print("say AAA!")
class B:
def bb(self):
print("bb")
def say(self):
print("say BBB!")
class C(A,B):
def cc(self):
print("cc")
c=C()
print(C.mro()) #打印类的层次结构
c.say() #A在B前,先调用A,再调用B
7.super()获得父类定义
在子类中,如果想要获得父类的方法时,需要通过super()来实现
#测试super()
class A:
def say(self):
print("A:",self)
class B(A):
def say(self):
A.say(self)
print("B:",self)
B().say()
#将会输出A:<__main__.B object at 0x021A6610>和B:<__main__.B object at 0x021A6610>
#-------------或者是使用以下方式获得父类的方法-----------
class B(A):
def say(self):
super().say() #使用super()替代父类,以获取父类方法
print("B:",self)
B().say() #输出的效果如上
(二)同一方法调用多态
- 多态是指同一方法调用,由于对象不同可能产生不同行为。
- 注:
1.多态是方法的多态,属性没有多态
2.多态的存在有两个必要条件:继承、方法重写
class Man:
def eat(self):
print("饿了,快吃饭")
class Chinese(Man):
def eat(self):
print("筷子")
class English(Man):
def eat(self):
print("叉子")
class Indian(Man):
def eat(self):
print("右手")
def manEat(m):
if isinstance(m,Man): #多态。如果传进来的是Man的子类,则根据对象的不同调用不同的方法!
m.eat()
else:
print("不能吃饭")
manEat(Chinese())
manEat(English())
六、特殊方法和运算符重载
python的运算符实际上是通过调用对象的特殊方法实现的。比如:
a=20
b=30
c=a+b
d=a.__add_(b) #加号+的实质是调用了__add__()方法
print('c=',c)
print('d=',d) #两者的运算结果相同
每个运算符实际上都对应了相应的方法,统计如下:
这解释了为什么字符串也可以进行±*等等操作
常见的特殊方法统计如下:
- 运算符重载(实质是方法的重载)
class Person:
def __init__(self,name):
self.name=name
def __add__(self,other):
if isinstance(other,Person): #判断other是不是Person类型
return "{0}--{1}".format(self.name,other.name)
else:
return "不是同类对象,不能相加"
def __mul__(self,other):
if isintance(other,int): #判断other是不是int类型
return self.name*other
else:
return "传入的数据不是整数类型,不能相乘"
p1=Person("GA")
p2=Person("ca")
f=p1+p2
print(f)
print(f*3)
七、特殊属性
- obj.dict:获得类的属性字典
class c:
def __init__(self,age):
self.age=age
g=c(20)
print(g.__dict__) #将会输出{'age':20}
- obj.class:获得对象属于的类
class a:
def say(self):
print("不错")
class c(a):
def __init__(self,age):
self.age=age
g=c(20)
print(g.__class__) #将会输出类为c的地址
- obj.bases:当有多个父类时,将对象的父类的地址放在元组中
class a:
def say(self):
print("不错")
class b:
def say(self):
print("很好")
class c(a,b):
def __init__(self,age):
self.age=age
g=c(20)
print(g.__bases__) #将会输出a,b的地址组成的元组
- obj.base:当只有单个父类时,输出对象的父类的地址
- obj.mro:类层次结构
- obj.subclass():子类列表
class a:
def say(self):
print("不错")
class c(a):
def __init__(self,age):
self.age=age
g=c(20)
print(a.__subclass__()) #已知父类,输出子类。此处将会输出c的地址
八、对象的浅拷贝和深拷贝
- 变量赋值:形成两个变量(可以理解为标签),实质指向同一个对象(那么两个变量的地址仍然不变)
- 浅拷贝:只拷贝了源对象,没有拷贝儿子和孙子,拷贝对象和源对象仍然指向同一个儿子和孙子。因此源对象和拷贝对象的地址不同,但是子对象的地址相同
- 深拷贝:不仅拷贝了源对象,还拷贝了儿子和孙子。因此,源对象和拷贝对象的地址不同,其子对象的地址也不同
九、组合
- 继承:是“is a”的关系,如狗是动物
- 组合:是“has a”的关系,如手机拥有cpu
#继承
class A:
def say_a1(self):
print("a1")
class B(a):
pass
b=B()
b.say_a1 #类B继承了类A的方法
#组合
class C:
def __init__(self,a):
self.a=a
class D:
def say_d1(self,a):
print("我是{0}".format(a))
c=C(D()) #将类D对类C进行组合
c.D.say_d1() #可以通过对象c调用对象D的方法
十、设计模式
(一)工厂模式的实现
- 含义:设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候的固定做法。比较流行的是GOF(Goup Of Four)模式。
- 工厂模式:能够实现创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制
例子如下:
#测试工厂模式
class CarFactory:
def create_car(self,brand):
if brand =="奔驰":
return Benz()
elif brand=="宝马":
return BNW()
elif brand=="比亚迪":
return BYD()
else:
return "未知品牌,无法创建"
class Benz:
pass
class BNW:
pass
class BYD:
pass
factory=CarFactory()
c1=factory.create_car("奔驰")
c2=factory.create_car("比亚迪")
(二)单例模式的实现
- 核心作用:确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 优点:当一个对象的对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留于内存中,从而极大地降低开销。
class MySingleton: #单例对象需要重写new和init方法
__obj=None #类属性
__init__flag=True
def __new__(cls,*args,**kwargs):
if cls.__obj==NONE: #当类属性还没有建立时,创建一个类对象
cls.__obj=object.__new__(cls)
return cls.__obj
def __init__(self,name):
if MySingleton.__init__flag==True:
print("init...")
self.name=name
MySingleton.__init__flag==False
a=MySingleton("aa")
b=MySingleton("bb") #只会输出一次init...
print(a)
print(b) #a和b的地址都是一样的
#测试工厂模式的单例模式
class CarFactory:
__new__=None
__init__flag=True
def __new__(cls,*args,**kwargs):
if cls.__obj==NONE: #当类属性还没有建立时,创建一个类对象
cls.__obj=object.__new__(cls)
return cls.__obj
def __init__(self):
if CarFactory.__init__flag==True:
print("init CarFactory...")
CarFactory.__init__flag==False
def create_car(self,brand):
if brand =="奔驰":
return Benz()
elif brand=="宝马":
return BNW()
elif brand=="比亚迪":
return BYD()
else:
return "未知品牌,无法创建"
class Benz:
pass
class BNW:
pass
class BYD:
pass
factory1=CarFactory()
c1=factory1.create_car("奔驰")
c2=factory1.create_car("比亚迪")
factory2=CarFactory()
print(factory1)
print(factory2) #factory1和factory2的地址是一样的。