Python【系列教程】之類和對象

一、定義類,對象,實例方法和自動綁定self

class Person:
    hair = 'black'

    # 構造方法
    def __init__(self, name='Charlie', age=8):
        self.name = name
        self.age = age

    # 定義一個say方法
    def say(self, content):
        print(content)


p = Person()
print(p.name, p.age)

p.name = '李剛'
p.say('語言簡單,學習很容易')
print(p.name, p.age)
p.skills = ['programming', 'swimming']
print(p.skills)
del p.name
# 刪除p對象的name屬性,再次訪問的時候報錯
# print(p.name)


# 動態給對象增加方法
def info(self):
    print("--info函數--", self)


p.foo = info
p.foo(p)
p.bar = lambda self: print('--lambda表達式--', self)
p.bar(p)
"""上面分別用函數和lambda表達式爲p對象動態增加了方法,但是對於動態
增加的方法,python不會自動將方法調用者綁定到它們的第一個參數,因此程序必須手動爲第一個參數
傳入參數值"""
"""如果希望動態增加的方法也能自動綁定到第一個參數,則可藉助於types模塊下的MethodType進行包裝"""
def intro_func(self, content):
    print("我是一個人,信息爲:%s" % content);
# 導入MethodType
from types import MethodType
p.intro = MethodType(intro_func, p)
p.intro("生活在別處");

二、方法

class Bird:
    # 使用@Classmethod修飾的方法是類方法
    @classmethod
    def fly(cls):
        print('類方法fly:', cls)

    # 使用@staticmethod修飾的方法是靜態方法
    @staticmethod
    def info(p):
        print('靜態方法info:', p)


Bird.fly()
Bird.info("crazy")
b = Bird()
b.fly()
b.info("kit")

"""前面的靜態方法和類方法的本質就是函數裝飾器,
其中classmethod和staticmethod都是python內置的函數"""


def funA(fn):
    print('A')
    fn()  # 執行傳入的fn參數
    return 'end'


@funA
def funB():
    print('B')


print(funB)
"""上面的程序@funA修飾funB,這意味着程序要完成兩步操作
將funB作爲funA()的參數,也就是上面的粗體字碼相當於執行funA(funB)
將funB替換成第1步執行的結果,funA()執行完成後返回fkit,因此funB
就不再是函數,而是被替換成一個字符串
"""


def foo(fn):
    # 定義一個嵌套函數
    def bar(*args):
        print("====1====", args)
        n = args[0]
        print('====2====', n * (n - 1))
        # 查看傳給foo函數的fn函數
        print(fn.__name__)
        fn(n * (n - 1))
        print("*" * 15)
        return fn(n * (n - 1))

    return bar


@foo
def my_test(a):
    print('====my_test函數====', a)


print(my_test)
my_test(10)
my_test(6, 5)


# def auth(fn):
#     def auth_fn(*args):
#         print("模擬執行權限檢查")
#         fn(*args)
#
#     return auth_fn
def auth(fn):
    def auth_fn(*args):
        print("模擬執行權限檢查")
        fn(*args)

    return auth_fn

@auth
def test(a, b):
    print("asdasdasdasd啊實打實大蘇打")


test(20, 15)
"""上面介紹的這種在被修飾函數之前,之後,拋出異常後增加某種處理邏輯的方式
就是其他編程語言中的aop面向切面編程
"""

class item:
    print('正在定義item類')
    for i in range(10):
        if i % 2 == 0:
            print('偶數',1)
        else:
            print('奇數',1)


global_fn = lambda p:print('執行lambda表達式,p參數',p)
class category:
    cate_fn = lambda p:print('執行lambda表達式,p參數',p)
global_fn('fkit')
c = category()
c.cate_fn()

三、成員變量

# 1.使用property函數定義屬性
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def setSize(self, size):
        self.width, self.height = size

    def getSize(self):
        return self.width, self.height

    def delSize(self):
        self.width, self.height = 0, 0

    size = property(getSize, setSize, delSize, '用於描述矩形大小的屬性')


print(Rectangle.size.__doc__)
help(Rectangle.size)

rect = Rectangle(4, 3)
# 相當於調用了getSize方法
print(rect.size)

rect.size = 9, 7
print(rect.width)
print(rect.height)

# 刪除size,相當於刪除了width屬性和height屬性
del rect.size
print(rect.width)
print(rect.height)
"""在某些編程語言中,類似於這種property合成的屬性被稱爲計算屬性。這種屬性
並不真正存儲任何狀態,它的值其實是通過某種算法計算得到的,當程序對該屬性賦值時,
被賦的值也會被存儲到其他實例變量中
"""
"""還可以使用@property裝飾器來修飾方法,使之成爲屬性"""


class Cell:
    # 使用@property裝飾器來修飾方法,相當於爲該屬性設置getter方法
    @property
    def state(self):
        return self._state

    # 爲state屬性設置setter方法
    @state.setter
    def state(self, value):
        if 'alive' in value.lower():
            self._state = 'alive'
        else:
            self._state = 'dead'

    # 爲is_dead屬性設置getter方法
    # 只有getter方法的屬性是隻讀屬性
    @property
    def is_dead(self):
        return not self._state.lower() == 'alive'


c = Cell()
c.state = 'Alive'
print(c.state)
print(c.is_dead)


##################隱藏和封裝################
class User:
    def __hide(self):
        print("示範隱藏的hide方法")

    def getname(self):
        return self.__name

    def setname(self, name):
        if len(name) < 3 or len(name) > 8:
            raise ValueError('用戶名必須在3~8之間')
        self.__name = name
    name = property(getname,setname)

u = User()
#__hide其實是python悄悄修改了方法名,將__hide修改成了_類名__hide()
u._User__hide()

u._User__name = 'fk'
print(u.name)
"""如果程序希望python類中的某些成員隱藏起來,那麼只要讓該成員的名字以雙下劃線開頭即可。
即使通過這種機制實現了隱藏,其實也依然可以繞過去"""

四、類的繼承

# 1.類的繼承
class Fruit:
    def info(self):
        print('我是一個水果!重%g克' % self.weight)


class Food:
    def taste(self):
        print("不同食物的口感不同")


# 定義Apple類,繼承了Fruit和Food類
class Apple(Fruit, Food):
    pass


a = Apple()
a.weight = 5.6
# 調用水果類的info方法
a.info()
# 調用食物類的taste方法
a.taste()


class Item:
    def info(self):
        print("item中方法:", '這是一個商品')


class Product:
    def info(self):
        print("Product中方法:", "這是一個工業產品")


class Mouse(Item, Product):
    pass


m = Mouse()
m.info()
"""Mouse繼承了兩個類,兩個類具有相同的方法,但是Item排在Product的前面,
因此Item中定義的方法優先級更好,Python會優先到Item父類中搜尋方法,一旦
在Item父類中搜尋到目標方法,Python就不會繼續向下搜尋了"""


# 重寫父類的方法
class Bird:
    # Bird類的fly()方法
    def fly(self):
        print('我在天空裏自由自在的飛翔')


class Ostrich(Bird):
    def fly(self):
        print('我只能在地上奔跑')


# 創建Ostrich
os = Ostrich()
# 執行子類的fly方法,將輸出我只能在地上奔跑
os.fly()


class BaseClass:
    def foo(self):
        print('父類中定義的foo方法')


class SubClass(BaseClass):
    def foo(self):
        print("子類重寫父類中的foo方法")

    def bar(self):
        print('執行bar方法')
        self.foo()
        # 使用類名調用實例方法(未綁定方法)調用
        BaseClass.foo(self)


sc = SubClass()
sc.bar()


# 使用super函數重寫父類構造方法
class Employee:
    def __init__(self, salary):
        self.salary = salary

    def work(self):
        print('普通員工正在寫代碼,工資是:', self.salary)


class Customer:
    def __init__(self, favorite, address):
        self.favorite = favorite
        self.address = address

    def info(self):
        print('我是一個顧客,我的愛好是:%s,地址是: %s' % (self.favorite, self.address))
class Manager(Employee,Customer):
    def __init__(self,salary,favorite,address):
        super(Manager,self).__init__(salary)
        Customer.__init__(self,favorite,address)
m=Manager(25000,'it產品','廣州')
m.work()
m.info()

五、python的動態性

############################################2.動態屬性與_slots_
#動態的給Cat類添加方法
class Cat:
    def __init__(self,name):
        self.name = name
def walk_func(self):
    print('%s 慢慢地走過一片草地' % self.name)
d1 =Cat('Garfield')
d2 =Cat('Kitty')
Cat.walk = walk_func
d1.walk()
d2.walk()

class Dog:
    __slots__ = ('walk','age','name','foo')
    def __init__(self,name):
        self.name = name
    def test(self):
        print('預先定義的test方法')
d = Dog('snooy')
#只允許爲實例動態的添加walk,age,name這三個屬性或者方法
from types import  MethodType
d.walk = MethodType(lambda self:print('%s 正在慢慢的走'),d)
d.age = 5
d.walk()
d.foo = 30
#__slots__並不限制通過類來動態添加方法
Dog.bar = lambda self: print('abc')
d.bar()

#####################3.使用type()函數定義類
def fn(self):
    print("this is a 函數")
Dog = type('Dog',(object,),dict(walk=fn,age=6))
#創建Dog對象
d = Dog()
print(type(d))
print(type(Dog))
d.walk()
print(Dog.age)
"""type定義類的時候可以指定三個參數
參數一:創建的類名
參數二:該類繼承的父類集合,由於python 支持多繼承,因此此處使用元組指定它的多個父類。即使實際
只有一個父類,也需要使用元組語法(必須要多一個逗號)
參數三:該字典對象爲該類綁定的類變量和方法。其中字典key就是變量名或者方法名,
如果字典的value是普通值,那就代表類變量,如果字典的value是函數,則代表方法。
"""


###########################4.使用metaclass
"""如果希望創建某一批類全部具有某種特徵,可通過metaclass來實現,使用metaclass可在創建類的
時候動態修改類定義,爲了使用metaclass動態修改類定義,程序需要先定義metaclass,metaclass應該繼承
type類,並重寫__new__()方法"""
class ItemMetaclass(type):
    #cls代表被動態修改的類
    #name代表被動態修改的類名
    #bases代表被動態修改的類的所有父類
    #attr代表被動態修改的類的所有屬性,方法組成的字典
    def __new__(cls, name,bases,attrs):
        attrs['cal_price'] = lambda self: self.price * self.discount
        return type.__new__(cls,name,bases,attrs)

class Book(metaclass=ItemMetaclass):
    __slots__ = ('name','price','_discount')
    def __init__(self,name,price):
        self.name = name
        self.price = price
    @property
    def discount(self):
        return self._discount
    @discount.setter
    def discount(self,discount):
        self._discount = discount
class CellPhone(metaclass=ItemMetaclass):
    __slots__ = ('price', '_discount')
    def __init__(self, price):
        self.price = price
    @property
    def discount(self):
        return self._discount
    @discount.setter
    def discount(self, discount):
        self._discount = discount
"""上面定義的Book類和CellPhone類都指定了metaclass信息,因此當
python解釋器在創建這兩個類的時候,ItemMetaClass的__new__方法就會被調用,
用於修改這兩個類,動態增加cal_price方法"""
b= Book("瘋狂python講義",89)
b.discount = 0.76
print(b.cal_price())

cp = CellPhone(2399)
cp.discount = 0.85
print(cp.cal_price())
"""metaclass這個功能在開發一些基礎性框架時非常有用,程序可以通過
使用metaclass爲某一批需要具有通用功能的類添加方法"""

六、多態

#################1.多態性
class Bird:
    def move(self, field):
        print('鳥在%s上自由的飛翔' % field)


class Dog():
    def move(self, field):
        print('狗在%s裏飛快地奔跑' % field)


x = Bird()
# 調用x變量
x.move('天空')
x = Dog()
x.move('草地')


class Canvas:
    def draw_pic(self, shape):
        print('---開始繪圖---')
        shape.draw(self)


class R:
    def draw(self, canvas):
        print('在%s上繪製矩形' % canvas)


class T:
    def draw(self, canvas):
        print('在%s上繪製三角形' % Canvas)


class Circle:
    def draw(self, canvas):
        print('在%s上h繪製圓形' % canvas)


c = Canvas()
c.draw_pic(R())
c.draw_pic(T())

#################2.檢查類型
"""python提供了兩個函數來檢查類型
issubclass(cls,class_or_tuple):檢查cls是否是後一個類或者元組包含的多個類中任意類的子類
isinstance(obj,class_or_tuple):檢查obj是否是後一個類或者元組包含的多個類中任意類的對象
通過使用上面兩個函數,程序可以方便地先執行檢查,然後才調用方法,這樣可以保證程序不會出現
意外情況
"""
hello = "hello"
print(isinstance(hello, str))

print(issubclass(str, object))

print(isinstance(hello, tuple))


class A:
    pass


class B:
    pass


class C(A, B):
    pass


print(C.__bases__)

print(B.__subclasses__())

"""類名.__bases__: 可查看該類所有直接父類
類名.__subclasses__():通過該方法可以查看該類的所有直接子類"""

########################3.枚舉類
"""枚舉的兩種方式:
(1)直接使用Enum列出多個枚舉值來創建枚舉類
(2)通過繼承Enum基類來派生枚舉類
"""
import enum
Season = 'Season'
Season = enum.Enum(Season, ('SPRING', 'SUMMER', 'FALL', 'WINTER'))
print(Season.SPRING)
print(Season.SPRING.name)
print(Season.SPRING.value)
print(Season['SUMMER'])
print(Season(3))

for name,member in Season.__members__.items():
    print(name,'=>',member,',',member.value)

class Orientation(enum.Enum):
    #爲序列值指定value值
    east = '東'
    south= '南'
    west = '西'
    north = '北'
    def info(self):
        print('這是一個代表方向【%s】的枚舉' % self.value)
print(Orientation.south)
print(Orientation.south.value)
print(Orientation['west'])
print(Orientation('南'))
print(Orientation.east.info())

for name,member in Orientation.__members__.items():
    print(name,'=>',member,',',member.value)

#枚舉的構造器
import  enum
class Gender(enum.Enum):
    MALE = '男','陽剛之力'
    FEMALE = '女','柔順之美'
    def __init__(self,cn_name,desc):
        self._cn_name = cn_name
        self._desc = desc
    @property
    def desc(self):
        return self._desc
    @property
    def cn_name(self):
        return self._cn_name
print(Gender.FEMALE.name)
print(Gender.FEMALE.value)
print(Gender.FEMALE.cn_name)
print(Gender.FEMALE.desc)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章