文章目录

  • ​​1.对象​​
  • ​​2.OO特征​​
  • ​​3.self​​
  • ​​4.\_\_init__​​
  • ​​5.公有和私有​​
  • ​​6.继承​​
  • ​​7.类的组合关系​​
  • ​​8.类、类对象和实例对象​​
  • ​​9.与类和对象相关的一些相关的BIF​​
  • ​​10.构造和析构​​

1.对象

  • 在win下如何调试使用python写好的一个类?
    使用IDLE打开python代码

    执行Run中的Run Module调试写好的类
  • test.py代码如下:
class Turtle: # Python 中的类名约定以大写字母开头
"""关于类的一个简单例子"""
# 属性
color = 'green'
weight = 10
legs = 4
shell = True
mouth = '大嘴'

# 方法
def climb(self):
print("我正在很努力的向前爬......")

def run(self):
print("我正在飞快的向前跑......")

def bite(self):
print("咬死你咬死你!!")

def eat(self):
print("有得吃,真满足^_^")

def sleep(self):
print("困了,睡了,晚安,Zzzz")
  • 对象中的属性和方法,在编程中实际是变量(属性)和函数(方法)。

2.OO特征

  • OO(面向对象)的特征
    封装:对外部隐藏对象的工作细节
    继承:子类自动共享父类之间数据和方法的机制
    多态:可以对不同类的对象调用相同的方法,产生不同的效果
定义一个带列表类MyList,继承list,则列表的功能它都可以使用
>>> class MyList(list):
pass

>>> list2 = MyList()

>>> list2.append(5)
>>> list2.append(6)

>>> list2.append(1)
>>> list2
[5, 6, 1]
>>> list2.sort()
>>> list2
[1, 5, 6]

3.self

  • 要求:在类定义的时候,将self写入到第一个参数
  • Python的self其实就相当于C++的this指针。由同一个类可以生产无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么python就知道需要操作哪个对象的方法了。
  • eg:
>>> class Ball:
def setName(self,name):
self.name = name
def kick(self):
print('我叫%s,该死的,谁踢我。。。' % self.name)


>>> a = Ball()

>>> a.setName('球A')
>>> b = Ball()

>>> b.setName('球B')

>>> a.kick()
我叫球A,该死的,谁踢我。。。
>>> b.kick()
我叫球B,该死的,谁踢我。。。

4.__init__

  • python的这些具有魔法的方法,总是被双下划线所包围,例如__init__(),即构造方法,也称构造函数,这个方法会在对象被创建时自动调用。其实,实例化对象时是可以传入参数的,这些参数会自动传入__init__()方法中,可以通过重写这个方法来自定义对象的初始化操作
  • eg:
>>> class Ball():
def __init__(self,name):
self.name = name
def kick(self):
print('我叫%s,该死的,谁踢我。。。' % self.name)


>>> b = Ball('小土豆')
>>> b.kick()
我叫小土豆,该死的,谁踢我。。。

5.公有和私有

  • 为了实现定义私有变量,只需要在变量名或函数名前加上"__"两个下划线,那么这个函数或变量就会变成私有的了:
>>> class Person:
__name = '亦我飞也'
def getName(self):
return self.__name


>>> p = Person()
>>> p.__name
Traceback (most recent call last):
File "<pyshell#72>", line 1, in <module>
p.__name
AttributeError: 'Person' object has no attribute '__name'
>>> p.getName()
'亦我飞也'
  • pyton的私有变量其实是伪私有
    实际上在外部使用“_类名__变量名“即可访问双下划线开头的私有变量了
>>> p._Person__name
'亦我飞也'

6.继承

  • 继承方法
class DerivedClassName(BaseClassName):
  • 一个子类可以继承它的父类的所有属性和方法
>>> class Parent:
def hello(self):
print('正在调用父类的方法。。。')



>>> class Child(Parent): #子类继承父类
pass #直接往下执行

>>> p = Parent()
>>> p.hello()
正在调用父类的方法。。。
>>> c = Child()
>>> c.hello()
正在调用父类的方法。。。
  • 如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法和属性(即子类方法属性改变,父类是不变的)
    解决办法:
    (1)调用未绑定的父类方法
    (2)使用super函数(推荐使用,因为只需要改派生类中的参数:基类名字就行了,不需要按照(1)要改很多Fish.init(self)中的Fish)
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0,10)
self.y = r.randint(0,10)

def move(self):
self.x -= 1
print('我的位置是:',self.x,self.y)


class Goldfish(Fish):
pass

class Garp(Fish):
pass

class Shark(Fish):
def __init__(self):
Fish.__init__(self) ##(1)调用未绑定的父类方法,self是子类的实例对象
self.hungry = True

def eat(self):
if self.hungry:
print('吃货的梦想就是天天有的吃')
self.hungry = False
else:
print('太撑了,吃不下了!')

Fish.__init__(self) ##(1)调用未绑定的父类方法,self是子类的实例对象
其实就是Fish.__init__(shark)

>>> fish = Fish()
>>> fish.move()
我的位置是: -1 10
>>> fish.move()
我的位置是: -2 10
>>> goldfish = Goldfish()
>>> goldfish.move()
我的位置是: 2 3
>>> goldfish.move()
我的位置是: 1 3
>>> shark = Shark()
>>> shark.eat()
吃货的梦想就是天天有的吃
>>> shark.eat()
太撑了,吃不下了!
>>> shark.move() #报错原因时因为子类重写构造函数,覆盖了父类D的构造函数
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
shark.move()
File "D:\python3.3.2\小甲鱼python\python程序\第三十八节课\fish.py", line 8, in move
self.x -= 1
AttributeError: 'Shark' object has no attribute 'x'

注:继承父类属性的子类,其变量值只属于当前子类,是子类的局部变量

(1)修改方法1:报错修改部分解决方法一:调用未绑定的父类方法

(P36-P41)对象:OO特征,self ,__init__ ,公有和私有 ,继承 ,类的组合关系 ,类、类对象和实例对象 ,BIF,构造和析构_子类

>>> shark = Shark()
>>> shark.move()
我的位置是: 2 1
>>> shark.move()
我的位置是: 1 1

(2)修改方法2:

使用super函数(super函数会帮我们自动找到基类的方法,而且还自动为我们传入self参数)

Fish.init(self) 改为super().init

(P36-P41)对象:OO特征,self ,__init__ ,公有和私有 ,继承 ,类的组合关系 ,类、类对象和实例对象 ,BIF,构造和析构_父类_02

>>> shark = Shark()
>>> shark.move()
我的位置是: 1 1
>>> shark.move()
我的位置是: 0 1
  • 多重继承:不建议使用
class DerivedClassName(Base1, Base2, Base3):

……

实例:子类c同时继承基类Base1和基类Base2

>>> class Base1:
def fool1(self):
print('我是fool1,我为Base1代言。。。')


>>> class Base2:
def fool2(self):
print('我是fool2,我为Base2代言。。。')


>>> class C(Base1,Base2):
pass

>>> c = C()
>>> c.fool1()
我是fool1,我为Base1代言。。。
>>> c.fool2()
我是fool2,我为Base2代言。。。

7.类的组合关系

  • 组合(将需要的类一起进行实例化并放入新的类中),非继承关系的几个类放到一起
  • eg:
class Turtle:
def __init__(self,x):
self.num = x

class Fish:
def __init__(self,x):
self.num = x

class Pool:
def __init__(self,x,y):
self.turtle = Turtle(x)
self.fish = Fish(y)

def print_num(self):
print('水池里一共有乌龟 %d 条,鱼 %d 条' % (self.turtle.num,self.fish.num))

>>> pool = Pool(5,2)
>>> pool.print_num()
水池里一共有乌龟 5 条,鱼 2 条

8.类、类对象和实例对象

  • 类、类对象和实例对象
  • eg:以下例子可见,对实例对象c的count属性赋值后,就相当于覆盖了类对象C的count属性。如果没有赋值覆盖,那么引用的是类对象的count属性
class C:
count = 0;

>>> a = C()
>>> b = C()
>>> c = C()
>>> print(a.count,b.count,c.count)
0 0 0
>>> c.count += 10
>>> print(a.count,b.count,c.count)
0 0 10
>>> C.count += 100
>>> print(a.count,b.count,c.count)
100 100 10
  • 另外,如果属性的名字跟方法名相同,属性会覆盖方法:
    结论:不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展;用不同的词性命名,如属性名用名词、方法名用动词,并使用骆驼命名法等。
>>> class C:
def x(self):
print('X-man')

>>> c = C()
>>> c.x()
X-man
>>> c.x = 1 #新定义对象c的一个x属性,并赋值为1
>>> c.x
1
>>> c.x() #可见,方法x()已经被属性x给覆盖了
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
c.x()
TypeError: 'int' object is not callable
  • eg:到底什么是绑定?
    python严格要求需要有实例才能被调用,即绑定概念
>>> class BB:
def printBB(): #缺少self,导致无法绑定具体对象
print('no zuo no die')

>>> BB.printBB()
no zuo no die
>>> bb = BB()
>>> bb.printBB() #出现错误原因是由于绑定机制,自动把bb对象作为第一个参数传入,实际:bb.printBB(bb)
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given
  • 测试:
    实例对象叫dd,类对象叫CC,类也叫CC;
    dd.__dict__显示实例对象属性;
    CC.__dict__显示类对象属性,但不显示魔法方法;
    类中定义的属性,方法是静态变量。就算类对象被删除了,他们依旧是存放在内存中的。只有在程序退出的时候,该静态变量才会被释放。
    大多数情况下,应该考虑使用实例属性,self.x相当于dd.x,self相当于实例的名字,.什么相当于实例的空间了。类属性仅仅用来跟踪与类相关的值。

9.与类和对象相关的一些相关的BIF

  • issubclass(class, classinfo)
    如果第一个参数是第二个参数的一个子类,则返回True,否则返回False:
    1️⃣一个类被认为是其自身的子类。
    2️⃣classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则返回True。
    3️⃣在其他情况下,会抛出一个TypeError异常。
>>> class A:
pass

>>> class B(A):
pass

>>> issubclass(B,A)
True
>>> issubclass(B,B) #一个类被认为是其自身的子类
True
>>> issubclass(B,object) # object是所有类的基类
True
>>> class C:
pass

>>> issubclass(B,C)
False
  • isinstance(object, classinfo)
    如果第一个参数是第二个参数的实例对象,则返回True,否则返回False:
    1️⃣如果object是classinfo的子类的一个实例,也符号条件。
    2️⃣如果第一个参数不是对象,则永远返回False。
    3️⃣classinfo可以是类对象组成的元组,只要object是其中任何一个候选对象的实例,则返回True。
    4️⃣如果第二个参数不是类或者由类对象组成的元组,会抛出一个TypeError异常。
>>> issubclass(B,C)       注:第一个参数如果不是对象,则永远返回False
False
>>> b1 = B()
>>> isinstance(b1,B)
True
>>> isinstance(b1,C)
False
>>> isinstance(b1,A)
True
>>> isinstance(b1,(A,B,C))
True
  • hasattr(object, name)
    attr即attribute的缩写,属性的意思。其作用就是测试一下对象里是否有指定的属性。
    第一个参数是对象,第二个参数是属性名(属性的字符串名字),举个例子:
>>> class C:
def __init__(self,x=0):
self.x = x


>>> c1 = C()
>>> hasattr(c1,'x') #注意,属性名要用引号括起来
True
  • getattr(object, name[, default])
    返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数)的值;若没有设置default参数,则抛出AttributeError异常。
>>> class C:
def __init__(self, x=0, y=1):
self.x = x
self.y = y

>>> c1 =C()
>>> getattr(c1, 'x')
0
>>> getattr(c1, 'y')
1
>>> getattr(c1, 'z')
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
getattr(c1, 'z')
AttributeError: 'C' object has no attribute 'z'
>>> getattr(c1, 'z', '您访问的属性不存在')
'您访问的属性不存在'
  • setattr(object, name, value)
    用来设置对象中指定属性的值,如果指定的属性不存在,则新建属性并赋值。
>>> setattr(c1,'y','FishC')
>>> getattr(c1,'y')
'FishC'
  • elattr(object, name)
    用来删除对象中指定的属性,如果属性不存在,则抛出AttributeError异常。
>>> delattr(c1,'y')
>>> delattr(c1,'Z')
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
delattr(c1,'Z')
AttributeError: Z
  • property(fget=None, fset=None, fdel=None, doc=None)
    用来通过属性来设置属性。
>>> class C:
def __init__(self,size =10):
self.size = size
def getSize(self):
return self.size
def setSize(self,value):
self.size = value
def delSize(self):
del self.size
x=property(getSize,setSize,delSize)



>>> c = C()
>>> c.x #调用getSize()
10
>>> c.x = 12 #调用x=property(getSize,setSize,delSize)
>>> c.x
12
>>> c.size
12
>>> del c.x #删除属性x()
>>> c.size
Traceback (most recent call last):
File "<pyshell#53>", line 1, in <module>
c.size
AttributeError: 'C' object has no attribute 'size'
  • property()函数中的三个函数分别对应的是获取属性的方法、设置属性的方法以及删除属性的方法,这样一来,外部的对象就可以通过访问x的方式,来达到获取、设置或删除属性的目的。
    当需要更改上例中的getSize、setSize或delSize函数的名称时,如果这些方法是作为接口让用户调用的,那么对用户而言就要修改自己调用的方法名,很麻烦,使用了proprty()后,用户就不需担心这种问题了。

10.构造和析构

  • 两个构造方法
    (1)__ init__(self[,…])
    这个方法一般用于初始化一个类。
    但是,当实例化一个类的时候, __ init__()并不是第一个被调用的, 第一个被调用的是__ new__()。
>>> class Rectangle:
def __init__(self,x,y):
self.x = x
self.y = y
def getPeri(self):
return (self.x + self.y) * 2
def getArea(self):
return self.x * self.y
>>> rect = Rectangle(5,2)
>>> rect.getPeri()
14
>>> rect.getArea()
10

一般在需要进行初始化的时候才重写__ init__()方法。而且要记住该方法的返回值一定是None(或者也可以理解没有返回值)。

  • 两个构造方法
    (2)__ new__(cls[,…])重写基类方法
    __ new__()方法是创建类实例的方法, 创建对象时调用, 返回当前对象的一个实例。
    下面是当继承一个不可变的类型的时候,它的特征就显得尤为重要了。
    其实,new()才是在一个对象实例化时候所调用的第一个方法,它的第一个参数是这个类(cls),而其他的参数会直接传递给__init__()方法
__new__(cls[, ...])

>>> class CapStr(str):
def __new__(cls,string):
string = string.upper()
return str.__new__(cls,string)


>>> a = CapStr('hello world')
>>> a
'HELLO WORLD
  • del(self) 当对象将要被销毁的时候,这个方法就会被调用。但要注意,并非del x就相当于调用x.del()。
    (1)Python中的垃圾回收是以引用计数为主,分代收集为辅。引用计数的缺陷是循环引用的问题。
    (2)在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存。
>>> class C:
def __init__(self):
print('我是__init__方法,我被调用了...')
def __del__(self):
print('我是__del__方法,我被调用l...')


>>> c1 = C() #创建对象c1
我是__init__方法,我被调用了...
>>> c2 = c1
>>> c3 = c2
>>> del c1
>>> del c2
>>> del c3 #删除c3时,对象c1才会彻底被删除(即没有标签指向对象c1时,其才会被回收),引用计数=0时被删除
我是__del__方法,我被调用l...