python基础_day08

方法没有重载
在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含 3个部分:方法名、参数数量、参数类型。
Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python 中是没有方法的重载的。定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法的重载。
如果我们在类体中定义了多个重名的方法,只有最后一个方法有效。
建议:不要使用重名的方法!Python 中方法没有重载。

方法的动态性
Python 是动态语言, 我们可以动态的为类添加新的方法, 或者动态的修改类的已有的方法。

私有属性和私有方法(实现封装)
Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有
属性和私有方法,有如下要点:

  1. 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)。
  2. 类内部可以访问私有属性(方法)
  3. 类外部不能直接访问私有属性(方法)
  4. 类外部可以通过“实例_类名__私有属性(方法)名”访问私有属性(方法)
    例如:
e._Employee__age

【注】方法本质上也是属性!只不过是可以通过()执行而已。所以,此处讲的私有属性和公有属性,也同时讲解了私有方法和公有方法的用法。如下测试中,同时也包含了私有方法和公有方法的例子。

@property 装饰器
@property 可以将一个方法的调用方式变成“属性调用”。(不用带方法后的括号,看起来像属性调用)下面是一个简单的示例,让大家体会一下这种转变:

class People:
    def __init__(self,name,salary):
        self.name=name
        self.__salary=salary

    @property
    def salary(self):
        return self.__salary
    @salary.setter
    def salary(self,salary):
        if (1000 < salary <= 50000):
            self.__salary = salary
        else:
            print("输入错误!薪水在1000-50000范围内")

'''
    def get_salary(self):
        return self.__salary
    def set_salary(self,salary):
        if (1000 <salary <= 50000):
            self.__salary=salary
        else:
            print("输入错误!薪水在1000-50000范围内")

p1=People("lihelin",30000)
print(p1.get_salary())
p1.set_salary(-20000)
'''
p1=People("lihelin",30000)
print(p1.salary)
p1.salary=-20000
p1.salary=20000
print(p1.salary)
30000
输入错误!薪水在1000-50000范围内
20000

属性和方法命名总结
1) _xxx:保护成员,不能用“from module import * ”导入,只有类对象和子类对象能访问这些成员。
2)xxx:系统定义的特殊成员
2)__xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过 “对象名. _类名__xxx” 这种特殊方式访问。 Python 不存在严格意义的私有成员)
注:再次强调,方法和属性都遵循上面的规则。

类编码风格
1)类名首字母大写,多个单词之间采用驼峰原则。
2)实例名、模块名采用小写,多个单词之间采用下划线隔开。
3)每个类,应紧跟“文档字符串”,说明这个类的作用。
4)可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类
【实操】设计一个名为 MyRectangle 的矩形类来表示矩形。这个类包含:
(1) 左上角顶点的坐标:x,y
(2) 宽度和高度:width、height
(3) 构造方法:传入 x,y,width,height。如果(x,y)不传则默认是 0,如果 width和 height 不传,则默认是 100.
(4) 定义一个 getArea() 计算面积的方法
(5) 定义一个 getPerimeter(),计算周长的方法
(6) 定义一个 draw()方法,使用海龟绘图绘制出这个矩形

import turtle
t=turtle.Pen()
t.width(2)
t.color("red")
class MyRectangle:
    def __init__(self, x=0, y=0, width=100, height=100):
        self.x=x
        self.y=y
        self.width=width
        self.height=height

    def getArea(self):
        return self.width*self.height

    def getPerimeter(self):
        return 2*(self.width+self.height)

    def draw(self):
        t.penup()
        t.goto(self.x,self.y)
        t.pendown()
        t.goto(self.x+self.width,self.y)
        t.goto(self.x+self.width,self.y+self.height)
        t.goto(self.x,self.y+self.height)
        t.goto(self.x,self.y)
        turtle.done()

rec = MyRectangle(50,50)
print("面积:", rec.getArea())
print("周长:", rec.getPerimeter())
rec.draw()

面向对象三大特征介绍
Python 是面向对象的语言,也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。

·封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。
通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python 追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。

·继承
继承可以让子类具有父类的特性,提高了代码的重用性。
从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。

·多态
多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。

继承实现
继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。如果一个新类继承自一个设计好的类, 就直接具备了已有类的特征, 就大大降低了工作难度。已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。
语法格式
Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:

class 子类类名(父类 1[,父类 2...]):
	类体

如果在类定义中没有指定父类,则默认父类是 object 类。也就是说,object 是所有类的父类,里面定义了一些所有类共有的默认实现,比如:new()。
定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:

父类名.__init__(self, 参数列表)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.__age = age
    def work(self):
        print("努力上班!")
    def say_age(self):
        print("年龄我也不知道!")
class Student(Person):
    def __init__(self,name,age,score):
        Person.__init__(self,name,age)          #在子类构造函数中,调用父类构造函数
        self.score=score
    def say_message(self):
        print("姓名:{0}  年龄:{1}   分数:{2}".format(self.name,self._Person__age,self.score))
        #父类私有属性,通过(子类对象名._父类名__私有属性名)在子类调用

print(Student.mro())
s1=Student("lihelin",23,99)
s1.say_age()
s1.say_message()


[<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
年龄我也不知道!
姓名:lihelin  年龄:23   分数:99

类成员的继承和重写
1)成员继承:子类继承了父类除构造方法之外的所有成员。
2)方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”

查看类的继承层次结构
通过类的方法 mro()或者类的属性__mro__可以输出这个类的继承层次结构。

object 根类
object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。我们显然有必要深入研究一下 object 类的结构。对于我们继续深入学习 Python 很有好处。

dir()查看对象属性
为了深入学习对象,我们先学习内置函数 dir(),他可以让我们方便的看到指定对象所有的属性。
从上面我们可以发现这样几个要点:

  1. Person 对象增加了六个属性:
__dict__, __module__, __weakref__, age, name, say_age
  1. object 的所有属性,Person 类作为 object 的子类,显然包含了所有的属性。
  2. 我们打印 age、name、say_age,发现 say_age 虽然是方法,实际上也是属性。只不过,
    这个属性的类型是“method”而已。
age <class 'int'>
name <class 'str'>
say_age <class 'method'>

重写__str__()方法
object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数 str()经常用于 print()方法,帮助我们查看对象的信息,并且__str__()可以重写。

class Person:
	def __init__(self,name,age):
		self.name = name
		self.__age = age
	def __str__(self):
	'''将对象转化成一个字符串,一般用于 print 方法'''
		return "名字是:{0},年龄是{1}".format(self.name,self.__age)
p = Person("高淇",18)
print(p)
名字是:高淇,年龄是 18

多重继承
Python 支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。

MRO()
Python 支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索。
MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro()方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。

super()获得父类定义
在子类中,子类重写了父类的某个方法,此时父类中的该方法被覆盖。如果仍想要通过子类获得父类的方法时,我们可以通过 super()来做。super()代表父类的定义,不是父类对象。

super().父类方法名()

多态
多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。在现实生活中,我们有很多例子。比如:同样是调用人的休息方法,张三的休息是睡觉,李四的休息是玩游戏,高淇老师是敲代码。同样是吃饭的方法,中国人用筷子吃饭,英国人用刀叉吃
饭,印度人用手吃饭。
关于多态要注意以下 2 点:

  1. 多态是方法的多态,属性没有多态。
  2. 多态的存在有 2 个必要条件:继承、方法重写。

特殊方法和运算符重载
Python 的运算符实际上是通过调用对象的特殊方法实现的。比如:

a = 20
b = 30
c = a+b
d = a.__add__(b)
print("c=",c)
print("d=",d)

常见的特殊方法统计如下:

方法说明例子
__init__构造方法对象创建:p = Person()
__del__析构方法对象回收
__repr__, __str__打印,转换print(a)
__call__函数调用a()
__getattr__点号运算a.xxx
__setattr__属性赋值a.xxx = value
__getitem__索引运算a[key]
__setitem__索引赋值a[key]=value
__len__长度len(a)

每个运算符实际上都对应了相应的方法,统计如下:

运算符特殊方法说明
+__add__加法
-__sub__减法
<,<=,==__lt__,__le__,__eq__比较运算符
>,>=,!=__gt__,__ge__,__ne__比较运算符
|,^,&__or__,__xor__,__and__或、异或、与
<<,>>__lshift__,__rshift__左移、右移
*,/,%,//__mul__,__truediv__,__mod__,__floordiv__乘、浮点除、模运算(取余)、整数除
**__pow__指数运算
#测试运算符的重载
class Person:
def __init__(self,name):
self.name = name
def __add__(self, other):
if isinstance(other,Person):
return "{0}--{1}".format(self.name,other.name)
else:
return " 不 是 同 类 对 象 , 不 能 相 加"
def __mul__(self, other):
if isinstance(other,int):
return self.name*other
else:
return " 不 是 同 类 对 象 , 不 能 相 乘"
p1 = Person(" 高 淇")
p2 = Person(" 高 希 希")
x = p1 + p2
print(x)
print(p1*3)
高淇--高希希
高淇高淇高淇

特殊属性
Python 对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。这里我们列出常见的特殊属性:

特殊方法含义
obj.__dict__对象的属性字典
obj.__class__对象所属的类
class.__bases__类的基类元组(多继承)
class.__base__类的基类
class.__mro__类层次结构
class.__subclasses__()子类列表

对象的浅拷贝和深拷贝
变量的赋值操作:
只是形成两个变量,实际还是指向同一个对象。
浅拷贝:
Python 拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象。
深拷贝:
使用 copy 模块的 deepcopy 函数, 递归拷贝对象中包含的子对象。 源对象和拷贝对象所有的子对象也不同。

class Mobilephone:
    def __init__(self,cpu,screen):
        self.cpu=cpu
        self.screen=screen

class CPU:
    def calculate(self):
        print("计算中...")
        print("CPU对象:",self)
        
class Screen:
    def show(self):
        print("显示中...")
        print("Screen对象: ",self)

# 测试变量赋值
c1=CPU()
c2=c1
print(c1,c2)                  # m1与m2一样

# 测试浅拷贝
import copy
s1=Screen()
m1=Mobilephone(c1,s1)
m2=copy.copy(m1)
print(m1,m1.cpu,m1.screen)
print(m2,m2.cpu,m2.screen)
# 浅拷贝:当前拷贝对象m1与m2不一样,但子类对象m1.cpu和m2.cpu一样,m1.screen和m2.screen一样
# 也就是说浅拷贝,不拷贝当前对象的子类

# 测试深拷贝
m3=copy.deepcopy(m1)
print(m1,m1.cpu,m1.screen)
print(m3,m3.cpu,m3.screen)
# 深拷贝:当前拷贝对象m1与m2不一样,且子类对象m1.cpu和m2.cpu也不一样,m1.screen和m2.screen也不一样
# 也就是说浅拷贝,同时也要拷贝当前对象的子类

组合
“is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。 “is-a”关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
“has-a” 关系, 我们可以使用“组合” , 也能实现一个类拥有另一个类的方法和属性。 ”has-a”关系指的是这样的关系:手机拥有 CPU。 MobilePhone has a CPU。

设计模式_工厂模式实现
设计模式是面向对象语言特有的内容, 是我们在面临某一类问题时候固定的做法, 设计模式有很多种,比较流行的是:GOF(Goup Of Four)23 种设计模式。当然,我们没有必要全部学习,学习几个常用的即可。
对于初学者,我们学习两个最常用的模式:工厂模式和单例模式
工厂模式实现了创建者和调用者的分离, 使用专门的工厂类将选择实现类、 创建对象进行统一的管理和控制。

# 测试工厂模式
class CarFactory:
    def creat_car(self,brand):
        if brand=="Benz":
            return Benz()
        elif brand=="BMW":
            return BMW()
        elif brand=="BYD":
            return BYD()
        else:
            return "未知品牌无法创建"

class Benz:
    pass

class BMW:
    pass

class BYD:
    pass

factory=CarFactory()
c1=factory.creat_car("Benz")
c2=factory.creat_car("BMW")
c3=factory.creat_car("BYD")
print(c1,c2,c3)
<__main__.Benz object at 0x0000028626AC0708> <__main__.BMW object at 0x0000028626AC0748> <__main__.BYD object at 0x0000028626AC0788>

设计模式_单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式只生成一个实例对象, 减少了对系统资源的开销。 当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。
单例模式有多种实现的方式,我们这里推荐重写__new__()的方法。

# 测试单例模式
class MySingleton:
    __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 self.__init_flag:
            print("init...")
            self.name = name
            self.__init_flag=False

a=MySingleton("aa")
b=MySingleton("bb")
print(a,b)
print(a.name,b.name)
init...
<__main__.MySingleton object at 0x00000198BD51FAC8> <__main__.MySingleton object at 0x00000198BD51FAC8>
aa aa
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值