面向机器,抽象成机器指令,机器容易理解,代表是:汇编语言
面向过程:
做事情,排出步骤,第一步第二步,如果出现情况A则做什么处理,B什么处理,问题规模小,可以步骤化,按部就班处理。代表:C语言
面向对象:
什么是类?
类是抽象的,就是具有相同属性和功能的一类事物
一套方法论,可以认识分析世界,将万事万物抽象为类
什么是对象?
对象是类的具象,是一个实体 具体化的(instance、object)就是类的具体表现形式
属性,它是对象状态的抽象,是个人具备的特征。用数据结构来描述。
操作,它是对象行为的抽象,是一种能力。行为特征,用操作名和实现该操作的方法来描述。
每一个类是属性和操作的集合
每个实例都是不同的。
哲学
一切皆对象
对象是数据和操作的封装
对象是独立的,但是对象之间可以相互作用
目前OOP是最接近人类认知的编程范式
oo是 面向对象 P 是编程
面向对象3要素
1.封装
组装:将数据和操作组装到一起。
隐藏数据:对外只暴露一些接口,通过接口访问对象。
2.继承
多复用,父类有的就不用写了 == 继承来的就不用自己写了,
多继承少修改,OCP(Open-closed Principle开辟原则) 使用继承来改变,来体现个性
3.多态
面向对象编程最灵活的地方,动态绑定
python的类
定义
class ClassName:
语句块
- 必须使用class关键字
- 类名必须是用大驼峰命名 !!!这个很重要,自定义的都需要这个命名
- 类定义完成后,就产生了一个类对象,绑定到了标识符ClassName上
第一个类:
class MyClass: # 类对象 自定义类要使用大驼峰的规则
"""a example of class""" # MyClass.__doc__
x = 'abc' # 类的属性和变量
def foo(self): # 方法,操作,行为 类属性foo,也是方法
print('foo method')
# foo = lambda xxxx
MyClass.foo, MyClass.__name__, MyClass.__doc__
类对象,类的定义就会生成一个类对象
类的属性,类定义中的变量和类中定义的方法都是类的属性。在上例中 x、foo都是类的属性,__doc__也是属性
类变量,上例中x是类MyClass的变量
foo是方法对象method,不是普通的函数对象function了,它一般要求至少有一个参数。第一个参数可以是 self(self只是个惯用标识符,可以换名字),这个参数位置就留给了self。
self 指代当前实例本身
实例化:真正创建一个该类的对象(实例)
a = list() # 实例化
第二个类:
class Person:
def __init__(self,name,age): # 初始化-出厂配置,对生成的实例进行属性配置
"""实例属性"""
self.name = name
self.age = age
t = Person('Tom', 20) # 等式右边先做先构建一个实例,然后调给__init__ 执行
__init__ 是初始化函数,对实例进行初始化,第一个位置必须是self 例如:__init__ (self,name,age)
__init__ ()方法不能有返回值,也就是只能return None
实例对象instance
类实例化后一定会获得一个对象,就是实例对象
实例变量是每一个实例自己的变量,是自己独有的;
类变量是类的变量,是类的所有实例共享的属性和方法
python中每一种对象都拥有不同的属性。函数、类都是对象类,实例也是对象
特殊属性 | 含义 |
---|---|
__name__ | 对象名 |
__class__ | 对象的类型 |
__dict__ | 对象的属性的字典 |
__qualname__ | 类的限定名 |
每个实例都会为自己创建一个字典
第三个类:
class PerSon:
age = 3
def __init__(self, name):
self.name = name
print(PerSon.__class__, type(Person))
print(sorted(Person.__dict__.items()) , end='\n\n')
tom = PerSon('TOm') # 创建一个实例
print('-'* 30)
print(tom.__class__, type(Person)) # tom的class 为PerSon
print(sorted(tom.__dict__.items()) , end='\n\n')
总结:
是类的,也是这个类所有实例的,其实例都可以访问到;
是实例的,就是这个实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的所有实例可以共享这个变量。
实例属性的查找顺序
指的是实例使用 . 点号 来访问属性,会先找自己的 __dict__ ,如果没有,然后通过属性 __class–找到自己的 类,再去类的 __dict__ 中找
注意,如果实例使用 --dict–[变量名] 访问变量,将不会按照上面的查找顺序找变量了,这是指明使用字典的key 查找,不是属性查找。
练习题:
写一个装饰器函数,装饰一个类 给类增加一个NAME属性,并提取
def add_name(name): # 构建一个函数,并 克里化
def addnams(cls):
cls.NAME = name
return cls
return addnams
@add_name('abd')
class PerSon:
AGE = 3
#pass
print(PerSon.NAME)
print(PerSon.__dict__)
类方法和静态方法
类方法:
- 在类定义中,使用@classmethod装饰器修饰的方法
- 必须至少有一个参数,且第一个参数留给cls,cls指代类对象自身
- cls这个标识符可以是任意合法名称,但是不能修改
- 通过cls可以操作类的属性
- 可以通过实例调用,也可以通过类调用
class Person:
@classmethod # 一定会注入 类方法
def class_method(cls): # class_method = preson()
print('class = {0.__name__}({0})'.format(cls))
cls.HEIGHT = 170
@staticmethod # 静态方法,说明这个函数只归这个类管,里面没有任何参数注入
def static_methd(): # 什么参数都不用
print(Person.HEIGHT)
Person.class_method() # 类方法,通过classmethod直接作为第一参数注进去,通过类本身调用
Person().class_method() #类方法
分析:
class Person:
def method(self):
print("{}'s method".format(self))
@classmethod
def class_method(cls): # class_method = preson()
print('class = {0.__name__}({0})'.format(cls))
cls.HEIGHT = 170
@staticmethod # 静态方法
def static_method():
print(Person.HEIGHT)
print(1,Person.method(1))
print(2,Person.class_method())
print(3,Person.static_method())
tom = Person()
print(4, tom.method()) # 可以,直接注入实例
print(5, tom.class_method()) # 将Person注入这个cls
print(6, tom.static_methd()) #
类除了普通方法之后,都可以调用。
补充:
class Person:
def method(self):
print('{}s method'.format(self))
tom = Person()
tom.method() # 可以吗
Person.method() # 可以吗
Person.method(tom) # 可以吗
tom.__class__.method(tom) # 可以吗
#除了第二个其他都不行,用了类调用,没有实例绑定,缺少第一参数
@classmethod 装饰器想法:
假设我有一个学生类和一个班级类,想要实现的功能为:
执行班级人数增加的操作、获得班级的总人数;
学生类继承自班级类,每实例化一个学生,班级人数都能增加;
最后,我想定义一些学生,获得班级中的总人数。思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。所以用类方法是很合适的
代码:
class Student:
__num = 0
def __init__(self, name, age):
self.name = name
self.age = age
Student.add_studern() # 类方法调用自身
@classmethod
def add_studern(cls):
cls.__num += 1
@classmethod
def show_student(cls):
return cls.__num
a = Student('孙马', 38)
b = Student('pig', 19)
c = Student('bird', 20)
print(Student.show_student())
@staticmethod 装饰器
静态方法,逻辑上属于类,但是和类本身没有关系,不会涉及到类中的属性和方法操作,是独立的、单纯的函数,仅托管于某个类的名称空间中,便于使用和维护
譬如定义一个时间调用
import datetime
class TimeText:
def __init__(self, yead, mon, dat):
self.yead = yead
self.mon = mon
self.dat = dat
@staticmethod
def showtime():
return datetime.datetime.now()
t2 = TimeText.showtime()
print(t2)
t1 = TimeText(9021, 5, 18) # 设置时间,无法改变showtime的值
nowtime = t1.showtime()
print(nowtime)
所以说装饰器@staticmethod 装饰器相当于在类内部定义了个函数和在外部定义一个函数的作用是一样的
访问控制
先看一个例子:
class Person:
def __init__(self, name, age=18):
self.name = name
self.age = age
def growuo(self, i=1):
if i>0 and i<150:
self.age += i
p1 = Person('tom')
p1.growuo(150) # 超过了 150 则无变化
print(p1.age)
print('-'*30)
p1.age = 160 # 可以直接找到名字修改,限制值无用
print(p1.age)
所以要引出一个:** 私有(Private)属性**,通过这个属性可以解决这个问题
私有属性:
双下划线开头的属性名,就是私有属性
单下划线的方法只是开发者之间的约定,解释器不做任何改变。
实例:外部已经访问不到__age了
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def growuo(self, i=1):
if i>0 and i<150:
self.__age += i
p1 = Person('tom')
p1.growuo(20) # 超过了 150 则无变化
print(p1.__age)
那要怎么才能访问呢?
就要多定义个函数:
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def growuo(self, i=1):
if i>0 and i<150:
self.__age += i
def getage(self): # get到__age值
return self.__age
p1 = Person('tom')
print(p1.getage())
但是私有变量的本质是Python解释器会将其改名,装换名称为_类__变量名的名称,原来的名字访问不到,知道这个名字后还是可以直接修改的。用__dict__就可以直接看到
保护变量
在变量名前使用一个下划线,称为保护变量。解释器不做任何解释,就和普通变量名一样,只是开发者之间的约定。
私有方法
即私有函数
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age
def _getname(self):
return self.name
def __getage(self):
return self._age
p1 = Person('tom')
#print(Person.__dict__)
# print(tom._getname()) # 无这个名字
# print(tom.__getname())
print(tom.__dict__)
print(tom.__class__.__dict__)
print(tom._Person__getage())
补丁方法
测试的时候可以用:
test1.python文件
from test2 import Person
from test3 import get_score
def monkeypatch4Person():
Person.get_score = get_score
monkeypatch4Person() # 打补丁
if __name__ == "__main__":
print(Person().get_score())
test2.python文件
class Person:
def get_score(self):
# connect to mysql
ret = {'English':78, 'Chinese':86, 'History':82}
return ret
test3.python文件
def get_score(self):
return dict(name=self.__class__.__name__,English=88, Chinese=90, History=85)
属性装饰器 property
先看一个例子
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def age(self):# 查看年龄
return self.__age
def set_age(self, age): #设置年龄
self.__age = age
tom = Person('Tom')
print(tom.age())
tom.set_age(20)
print(tom.age())
有时候用户并不想通过set 来修改年龄,就想着tom.age = 20 . 简单的方法 这时候就需要@property 装饰器来实现 get() # 属性访问 set() # 属性设置 delate() #删除
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
@property
def age(self):# 查看年龄
return self.__age
@age.setter # 装饰器都要以age开头
def age(self, age): #设置年龄
self.__age = age
tom = Person('Tom')
print(tom.age)
tom.age = 20
print(tom.age)
或者可以
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def getage(self):# 查看年龄
return self.__age
def setage(self, age): #设置年龄
self.__age = age
age = property(getage, setage)
tom = Person('Tom')
print(tom.age)
tom.age = 20
print(tom.age)
方法重载:
其他面向对象的高级语言中,会有重载的概念。
所谓重载,就是同一个方法名,但是参数个数、类型不一样,就是同一个方法的重载。
但是 python 没有重载,也不需要重载
继承
先看例子:
class Animal(object):
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
class Cat(Animal):
pass
tom = Cat('tom', 'boy', 15)
print(tom.__dict__)
说明:class Cat(Animal) 这种形式就是从父类继承,括号中写上继承的类的列表。 继承可以让子类从父类获取特征(属性和方法)
Animal : 叫做父类,基类,超类
Cat : 叫做子类,派生类
继承可以分为单继承和多继承。
注意:如果类定义时,没有基类列表,等同于继承自object,object类是所有对象的根基类。
super() 函数是用于调用父类(超类)的一个方法
例子:
class Aniaml(object):
def __init__(self, age):
print('this is aniaml')
self.age = age
def show(self):
print(self.age)
class Cat(Aniaml):
# pass B实例一旦定义了初始化__init__方法,就不会自动调用父类的初始化__init__方法,需
# 要手动调用
def __init__(self, age, weight):
super().__init__(age) # 通过super() 调用父类的值
self.weight = weight
print('cat')
p1 = Cat(12, 50)
print(p1.__dict__)
##例子2:
class A(object):
def add(self, x):
y = x + 1
print(y)
class B(A):
def add(self, x):
super().add(x)
a1 = B()
a1.add(2)
例子三:定义接口
所以此时我们要用到对类的约束,对类的约束有两种:
1.提取父类. 然后在父类中定义好方法. 在这个方法中什么都不用干. 就抛一个异常就可以了. 这样所有的子类都必须重写这个方法. 否则. 访问的时候就会报错.
class Payname(object):
""" 此类什么都不做,就是制定一个标准,谁继承我,必须定义我里面的方法。
"""
#def moneys(self, moneys):
# pass
def pay(self,money):
raise Exception("你没有实现pay方法")
class Alpay(Payname):
def moneys(self, money):
print('use alpay is {}'.format(money))
class Baidu(Payname):
def moneys(self, money):
print('use Baidu is {}'.format(money))
class QQpay(Payname):
def moneys(self, money):
print('use QQpau is {}'.format(money))
class kekoay(Payname): # 不看继承乱定义
def pay(self, money):
print('use Qkekeo is {}'.format(money))
def moneys(obj, money): # 这个函数就是统一支付规则,这个叫做: 归一化设计。类里的函数都要一致
obj.moneys(money)
wan = QQpay()
ma = Alpay()
li = Baidu()
keke = kekoay() # 不看继承乱定义
# keke.pay(300)
moneys(wan, 400)
moneys(ma, 300)
moneys(li, 200)
moneys(keke,200) # 报错
小练习
1.随机整数生成类:
可以先设定一批生成数字的个数,可设定指定生成的数值的范围。运行时还可以调整每批生成数字的个数
"""方法一,随机生成,普通函数实现"""
import random
class RanDo:
def __init__(self, start=1, end=100, count=10):
self.start = start
self.end = end
self.__count = count
def ran(self):
return [random.randint(self.start, self.end) for i in range(self.__count)]
s1 = RanDo()
print(s1.ran())
---
s1 = RanDo(2, 10, 5)
print(s1.ran())
"""方法二,用工具类方法实现"""
class RanDom:
def __init__(self): pass
@classmethod # 调用类方法
def Generate(cls, start=1, end=10, count=5):
return [random.randint(start, end) for i in range(count)]
s1 = RanDom()
print(s1.Generate())
---
s2 = RanDom()
print(s2.Generate(1, 100, 10))
"""方法三,生成器实现"""
class RanDom:
def __init__(self, start=1, end=10, count=5):
self.start = start
self.end = end
self.count = count
self.a = self.test() # 实例有,方法在,可以调用
def test(self):
while True:
yield random.randint(self.start, self.end)
def Generate(self,count = 5):
# a = self.text()
# return [next(self.test()) for i in range(self.count)]
# return [next(self.a)) for i in range(self.count)]
# yield from (next(self.a) for i in range(self.count))
c = count if count > 0 and count < 100 else self._count
return [next(self.a) for i in range(c)] # 实现控制输出长度
s1 = RanDom()
print(s1.Generate())
#print(list(RanDom().Genderate))
print(RanDom().Generate(5))
"""方法四,属性装饰器proper"""
class RanDom:
def __init__(self, start=1, end=10, count=5):
self.start = start
self.end = end
self._count = count
self._gen = self._generate() # 实例有,方法在,可以调用
def _generate(self):
while True:
yield [random.randint(self.start, self.end) for i in range(self.count)]
def generate(self):
return next(self._gen)
@property
def count(self):
return self._count
@count.setter
def count(self, count):
self._count = count
r = RanDom()
print(r.generate())
r.count = 10
print(r.generate())
#使用上题中的类,随机生成20个数字,两两配对形成二维坐标系的坐标,把这些坐标组织起来,并打印输出
class Double: # 构造函数,里面缺两个值
def __init__(self, x, y):
self.x = x
self.y = y
r = RanDom()
r.tcount = 10
point = [Double(x, y) for x, y in zip(r.generate(), r.generate())]
for p in point:
print(p.x, p.y)
2.实现一个小游戏,可以攻击,显示血量。有武器库。可以调用武器库的武器来实现攻击
class Person:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
def attack(self, p1):
p1.hp = p1.hp - self.ad
print('{} attack {},{} have {} hp'.format(self.name, p1.name, p1.name, p1.hp))
def equip(self, wer):
self.wer = wer # 给人物装备上武器
class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad
def Weapo2(self, p1, p2):
p2.hp = p2.hp - p1.ad - self.ad
print('{} attack {},{} have {} hp'.format(p1.name, p2.name, p2.name, p2.hp))
y1 = Person('亚索', 20, 300)
g1 = Person('盖伦', 18, 400)
# y1.attack(g1)
# g1.attack(y1)
w1 = Weapon('疾风', 50)
# w1.Weapo2(y1, g1) # 直接调用函数不好,可以通过装备武器
y1.equip(w1)
print(y1.__dict__,y1.wer.ad) # 查看
y1.wer.Weapo2(y1, g1) # 只要是人物.equip这个方法,那么人物就封装了一个武器对象,
# 再利用武器对象调用其类中的weapon_attack方法
3.车辆信息,类里面品牌mark、颜色color 、价格price 、速度speed 。实现车辆管理,能增加车辆,显示全部车辆
class Caritem:
def __init__(self, mark, color, price, speed):
self.mark = mark
self.color = color
self.price = price
self.speed = speed
class CarInfo:
def __init__(self):
self.__a = []
def add_car(self, car: Caritem):
self.__a.append(car)
return self # 方便链式输出,返回的是实例本身
def show_car(self):
return [(x.mark, x.color, x.speed) for x in self.__a] # 可以过滤掉不要的信息
# return self.__a
c1 = Caritem('od', 'red', 100, 200)
c2 = Caritem('auto', 'yellow', 300, 400)
ne = CarInfo()
# print(ne.add_car(c1).add_car(c2).show_car()) 链式编程
ne.add_car(c1)
print(ne.show_car())
4、温度处理
实现华氏温度和摄氏温度的装换。
°C = 5 × (°F - 32) / 9
°F = 9 × °C / 5 + 32
完成以上转换后,增加与开氏温度的转换,K = °C +273.5
温度转换方法可以使用实例的方法,也可以使用类方法@classmethod,使用类方法的原因是,为了不创建对象,就可以直接进行温度转换计算,这个类设计像个温度工具类。
class Temperature:
# 构建一个可以保存下来的值,并可以实现装换,给你一个值,三个值全都输出
# 或者一个个值的算 一个个值算就要用到属性装饰器@property
def __init__(self, t, unit='c'):
self._c = None
self._f = None
self._k = None
if unit == 'c':
self._c = t
elif unit == 'f':
self._f = t
self._c = self.f2c(t)
elif unit == 'k':
self._k = t
self._c = self.k2c(t)
# if unit == 'c':
# self.t = t
# self.f = self.c2f(t)
# self.k = self.c2k(t)
@property
def c(self):
return self._c # c可能是None,所以说要将所有情况转化为c
@property
def f(self):
if self._f is None:
self._f = self.c2f(self._c)
return self._f
@property
def k(self):
if self._k is None:
self._k = self.c2k(self._c)
return self._k
@classmethod
def c2f(cls, c):
return 9 * c / 5 + 32
@classmethod
def f2c(cls, f):
return 5 * (f - 32) / 9
@classmethod
def k2c(cls, k):
return 273.15 - k
@classmethod
def c2k(cls, c):
return c + 273.15
@classmethod
def f2k(cls, f):
return cls.c2f(cls.f2c(f))
@classmethod
def k2f(cls, k):
return cls.c2k(cls.c2f(k))
print(Temperature.c2f(40))
print(Temperature.f2c(104))
print(Temperature.c2k(40))
t = Temperature(30, 'f') # 保存下来,需要的时候再调用
print(t.k, t.f) # 需要的时候再调用
购物车实现
class Color:
Red = 1
Yellow = 2
class Item:
def __init__(self, **kwargs):
# self.name = name
self._kw = kwargs # 关键字传参,构成一个字典
def __repr__(self): # 黑魔法,可以看到购物车的内容,在最后加
return '{}'.format(self._kw)
class Caritem:
def __init__(self):
self.newlist = []
def add_wu(self, car: Item):
self.newlist.append(car)
return self
def show_wu(self):
return self.newlist
# color 为了避免用户输入B, R red RED 等无法识别的字母,所以给color单独创建一个类
ca = Item(name='keke', color=Color.Red, value=100000)
ce = Item(name='eeee', color=Color.Red, value=100000)
aa = Caritem()
print(aa.add_wu(ca).add_wu(ce).show_wu())