python面試題~反射,元類,單例

1 什么是反射?以及應用場景?

test.py

def f1():print('f1')
def f2():print('f2')
def f3():print('f3')
def f4():print('f4')
a = 1
復制代碼
import test as ss
ss.f1()
ss.f2()
print(ss.a)
復制代碼

我們要導入另外一個模塊,可以使用import.現在有這樣的需求,我動態輸入一個模塊名,可以隨時訪問到導入模塊中的方法或者變量,怎么做呢?

imp = input(“請輸入你想導入的模塊名:”)
CC = __import__(imp) 這種方式就是通過輸入字符串導入你所想導入的模塊 
CC.f1()  # 執行模塊中的f1方法#或者用importlib模塊
import importlib
importlib.import_module(name="",package="")
復制代碼

上面我們實現了動態輸入模塊名,從而使我們能夠輸入模塊名并且執行里面的函數。但是上面有一個缺點,那就是執行的函數被固定了。那么,我們能不能改進一下,動態輸入函數名,并且來執行呢?

#dynamic.py
imp = input("請輸入模塊:")
dd = __import__(imp)
# 等價于import imp
inp_func = input("請輸入要執行的函數:")
f = getattr(dd,inp_func,None)#作用:從導入模塊中找到你需要調用的函數inp_func,然后返回一個該函數的引用.沒有找到就煩會None
f() # 執行該函數
復制代碼

反射機制 上面說了那么多,到底什么是反射機制呢?

??其實,反射就是通過字符串的形式,導入模塊;通過字符串的形式,去模塊尋找指定函數,并執行。利用字符串的形式去對象(模塊)中操作(查找/獲取/刪除/添加)成員,一種基于字符串的事件驅動!

  1. getattr()函數是Python自省的核心函數,具體使用大體如下:
class A: 
def __init__(self): self.name = 'zhangjing'#self.age='24'
def method(self): print"method print"
Instance = A() 
print getattr(Instance , 'name, 'not find') #如果Instance 對象中有屬性
name則打印self.name的值,否則打印'not find'
print getattr(Instance , 'age', 'not find') #如果Instance 對象中有屬性
age則打印self.age的值,否則打印'not find'
print getattr(a, 'method', 'default') #如果有方法method,否則打印其地址,
否則打印default 
print getattr(a, 'method', 'default')() #如果有方法method,運行函數并
打印None否則打印default 
復制代碼
  1. hasattr(object, name)
說明:判斷對象object是否包含名為name的特性(hasattr是通過調用getattr(ojbect, name)
是否拋出異常來實現的)
復制代碼
  1. setattr(object, name, value)
這是相對應的getattr()。參數是一個對象,一個字符串和一個任意值。字符串可能
會列出一個現有的屬性或一個新的屬性。這個函數將值賦給屬性的。該對象允許它提供
。例如,setattr(x,“foobar”,123)相當于x.foobar = 123。
復制代碼
  1. delattr(object, name)
與setattr()相關的一組函數。參數是由一個對象(記住python中一切皆是對象)和一
個字符串組成的。string參數必須是對象屬性名之一。該函數刪除該obj的一個由string
指定的屬性。delattr(x, 'foobar')=del x.foobar我們可以利用上述的四個函數,來對模塊進行一系列操作.r = hasattr(commons,xxx)判斷某個函數或者變量是否存在
print(r)  
setattr(commons,'age',18)  給commons模塊增加一個全局變量age = 18,創建成功返回none
setattr(config,'age',lambda  a:a+1)  //給模塊添加一個函數
delattr(commons,'age')//刪除模塊中某個變量或者函數
注:getattr,hasattr,setattr,delattr對模塊的修改都在內存中進行,并不會影響文件中真實內容。
復制代碼

2 metaclass作用?以及應用場景?

元類(metaclass)的理解和簡單運用 首先這里討論的python類,都基于繼承于object的新式類進行討論。

首先在python中,所有東西都是對象。這句話非常重要要理解元類我要重新來理解一下python中的類

class Trick(object):pass
復制代碼

當python在執行帶class語句的時候,會初始化一個類對象放在內存里面。例如這里會初始化一個Trick對象

這個對象(類)自身擁有創建對象(通常我們說的實例,但是在python中還是對象)的能力。

為了方便后續理解,我們可以先嘗試一下在新式類中最古老厲害的關鍵字type。

input:
class Trick(object):pass
print type('123')
print type(123)
print type(Trick)
output:
<type 'str'>
<type 'int'>
<class '__main__.Trick'>
復制代碼

可以看到能得到我們平時使用的 str, int, 以及我們初始化的一個實例對象Trick()

但是下面的方法你可能沒有見過,type同樣可以用來動態創建一個類

type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))

這個怎么用呢,我要用這個方法創建一個類 讓我們看下下面的代碼

input:
print type('Trick', (), {})output:
<class '__main__.Trick'>
同樣我們可以實例化這個類對象input:
print type('trick', (), {})()output:
<__main__.trick object at 0x109283450>
可以看到,這里就是一個trick的實例對象了。
復制代碼

同樣的這個方法還可以初始化創建類的父類,同時也可以初始化類屬性:

input:
class FlyToSky(object):passpw = type('Trick', (FlyToSky, ), {'laugh_at': 'hahahaha'})
print pw().laugh_at
print pw.__dict__
print pw.__bases__
print pw().__class__
print pw().__class__.__class__output:
hahahaha
{'__module__': '__main__', 'laugh_at': 'hahahaha', '__doc__': None}
(<class '__main__.FlyToSky'>,)
<class '__main__.Trick'>
<type 'type'>
復制代碼

下面我將依次理一下上面的內容,在此之前我必須先介紹兩個魔法方法:

__class__這個方法用于查看對象屬于是哪個生成的,這樣理解在python中的所有東西都是對象,類對象也是對象。如果按照以前的思維來想的話就是類是元類的實例,而實例對象是類的實例。__bases__這個方法用于得到一個對象的父類是誰,特別注意一下__base__返回單個父類,__bases__以tuple形式返回所有父類。
復制代碼

好了知道了這兩個方法我來依次說一下每行什么意思。

使用type創建一個類賦值給pw type的接受的三個參數的意思分辨是(類的名稱, 類是否有父類(), 類的屬性字典{})

這里初始化一個類的實例,然后嘗試去獲得類屬性 的laugh_at 屬性值,然后得到結果hahahaha

取一個pw的也就是我們常見類的類字典數據

拿到pw的父類,結果是我們指定的 FlyToSky

pw的實例pw()屬于哪個類初始化的,可以看到是class Trick

我們再看class trick是誰初始化的? 就是元類type了

什么是元類以及簡單運用

這一切介紹完之后我們總算可以進入正題

到底什么是元類?通俗的就是說,元類就是創建類的類。。。這樣聽起來是不是超級抽象?

來看看這個

Trick = MetaClass()
MyObject = Trick()
復制代碼

上面我們已經介紹了,搞一個Trick可以直接這樣

Trick = type('Trick', (), {}) 可以這樣其實就是因為,Type實際上是一個元類,用他可以去創建類。什么是元類剛才說了,元類就是創建類的類。也可以說他就是一個類的創建工廠。

類上面的__metaclass__屬性,相信愿意了解元類細節的盆友,都肯定見過這個東西,而且為之好奇。不然我不知道是什么支撐你看到這里的?。使用了__metaclass__這個魔法方法就意味著就會用__metaclass__指定的元類來創建類了。

class Trick(FlyToSky):pass
復制代碼

當我們在創建上面的類的時候,python做了如下的操作:

Trick中有__metaclass__這個屬性嗎?如果有,那么Python會在內存中通過__metaclass__創建一個名字為Trick的類對象,也就是Trick這個東西。如果Python沒有找到__metaclass__,它會繼續在自己的父類FlyToSky中尋找__metaclass__屬性,并且嘗試以__metaclass__指定的方法創建一個Trick類對象。如果Python在任何一個父類中都找不到__metaclass__,它也不會就此放棄,而是去模塊中搜尋是否有__metaclass__的指定。如果還是找不到,好吧那就是使用默認的type來創建Trick。

那么問題來了,我們要在__metaclass__中放置什么呢?答案是可以創建一個類的東西,type,或者任何用到type或子類化type的東西都行。

自定義元類

自定義類的的目的,我總結了一下就是攔截類的創建,然后修改一些特性,然后返回該類。是不是有點熟悉?沒錯,就是感覺是裝飾器干的事情,只是裝飾器是修飾一個函數,同樣是一個東西進去,然后被額外加了一些東西,最后被返回。

其實除了上面談到的制定一個__metaclass__并不需要賦值給它的不一定要是正式類,是一個函數也可以。要創建一個使所有模塊級別都是用這個元類創建類的話,在模塊級別設定__metaclass__就可以了。先寫一個來試試看

class UpperAttrMetaClass(type):def __new__(mcs, class_name, class_parents, class_attr):attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))uppercase_attrs = dict((name.upper(), value) for name, value in attrs)return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name, class_parents, uppercase_attrs)class Trick(object):__metaclass__ = UpperAttrMetaClassbar = 12money = 'unlimited'print(Trick.bar)
print(Trick.money)
復制代碼

3 用盡量多的方法實現單例模式。

單例模式 單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。

比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。

在 Python 中,我們可以用多種方法來實現單例模式

實現單例模式的幾種方式 使用模塊 其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc 文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:

class Singleton(object):def foo(self):pass
singleton = Singleton()
復制代碼

將上面的代碼保存在文件 mysingleton.py 中,要使用時,直接在其他文件中導入此文件中的對象,這個對象即是單例模式的對象

from a import singleton

使用裝飾器

def Singleton(cls):_instance = {}def _singleton(*args, **kargs):if cls not in _instance:_instance[cls] = cls(*args, **kargs)return _instance[cls]return _singleton@Singleton
class A(object):a = 1def __init__(self, x=0):self.x = xa1 = A(2)
a2 = A(3)
復制代碼

使用類

class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
復制代碼

一般情況,大家以為這樣就完成了單例模式,但是這樣當使用多線程時會存在問題

class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
import threading
def task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
程序執行后,打印結果如下:<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
復制代碼

看起來也沒有問題,那是因為執行速度過快,如果在init方法中有一些IO操作,就會發現問題了,下面我們通過time.sleep模擬

我們在上面__init__方法中加入以下代碼:

def __init__(self):import timetime.sleep(1)
復制代碼

重新執行程序后,結果如下

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
復制代碼

問題出現了!按照以上方式創建的單例,無法支持多線程

解決辦法:加鎖!未加鎖部分并發執行,加鎖部分串行執行,速度降低,但是保證了數據安全

import time
import threading
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(1)@classmethoddef instance(cls, *args, **kwargs):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)
for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
打印結果如下:<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
復制代碼

這樣就差不多了,但是還是有一點小問題,就是當程序執行時,執行了time.sleep(20)后,下面實例化對象時,此時已經是單例模式了,但我們還是加了鎖,這樣不太好,再進行一些優化,把intance方法,改成下面的這樣就行:

@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance
復制代碼

這種方式實現的單例模式,使用時會有限制,以后實例化必須通過 obj = Singleton.instance()

如果用 obj=Singleton() ,這種方式得到的不是單例

基于__new__方法實現(推薦使用,方便) 通過上面例子,我們可以知道,當我們實現單例時,為了保證線程安全需要在內部加入鎖

我們知道,當我們實例化一個對象時,是先執行了類的__new__方法(我們沒寫時,默認調用object.new),實例化對象;然后再執行類的__init__方法,對這個對象進行初始化,所有我們可以基于這個,實現單例模式

import threading
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):passdef __new__(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = object.__new__(cls)  return Singleton._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)def task(arg):obj = Singleton()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
復制代碼

采用這種方式的單例模式,以后實例化對象時,和平時實例化對象的方法一樣 obj = Singleton()

基于metaclass方式實現 """ 1.類由type創建,創建類時,type的__init__方法自動執行,類() 執行type的 __call__方法(類的__new__方法,類的__init__方法) 2.對象由類創建,創建對象時,類的__init__方法自動執行,對象()執行類的 call 方法 """

import threading
class SingletonType(type):_instance_lock = threading.Lock()def __call__(cls, *args, **kwargs):if not hasattr(cls, "_instance"):with SingletonType._instance_lock:if not hasattr(cls, "_instance"):cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)return cls._instanceclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
復制代碼

識別圖中二維碼,領取python全套視頻資料

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282611.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282611.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282611.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

僅有50Mb大小的cli即可搞定大廠才能玩的CloudIDE丨SmartIDE

作者&#xff1a;徐磊&#xff0c;開源云原生SmartIDE創始人、LEANOSFT創始人/首席架構師/CEO&#xff0c;微軟最有價值專家MVP/微軟區域技術總監Regional Director&#xff0c;華為云最有價值專家。從事軟件工程咨詢服務超過15年時間&#xff0c;為超過200家不同類型的企業提供…

idea-spark-sbt 打包jar

1、打開idea下的terminal窗口 2、只打包部分項目 sbt insight-import/clean insight-import/assembly 這表示只打包主目錄下的insight-import項目 &#xff0c;先清理&#xff08;clean&#xff09;再打包&#xff08;assembly&#xff09;不能用package&#xff0c;這個不會打…

操作Checkbox標簽

在前端開發中&#xff0c;少不了對Checkbox的操作。 常用的的方法有2個&#xff1a;.is()和.prop()方法。前者是判斷 checkbox的狀態&#xff0c;選不是未選。而后者為checkbox設置一個值&#xff0c;可以設置checkbox是true還是false。寫個小例子&#xff0c;練習一下&#xf…

memcache在項目中的應用

一 安裝memcache 具體流程這篇文章有寫到: 點擊 鏈接 二 在項目中應用memcache 為了減輕數據庫的查詢壓力,所以我們把一些不經常變動的數據進行緩存,用戶查詢時,如果查詢的要求是一樣的,我們就memcache緩存中讀取數據并返回去,如果查詢要求變了,我們再到數據庫中查詢,并將查…

點擊回退按鈕刷新頁面

瀏覽器用戶返回上一步&#xff0c;自動刷新window.οnunlοadfunction(){} 這個最簡單粗暴 方式一、<input type"hidden" id"refreshed" value"no"><script type"text/javascript">οnlοadfunction(){var edocument.getE…

Dapr 證書過期了怎么辦? 別慌,有救!

一、背景Dapr 默認證書有效時間是1年&#xff0c;證書過期后就不能執行相關控制面和數據面的交互了&#xff0c;如下圖&#xff1a;二、查看證書有效時間通過dapr mtls expiry 看到期時間&#xff0c;具體參見命令https://v1-7.docs.dapr.io/reference/cli/dapr-mtls/dapr-mtls…

python數據類型之間的轉換

對python內置的數據類型進行轉換時&#xff0c;可以使用內置函數&#xff0c;常用的類型轉換函數如下 python常用類型轉換函數函數格式使用示例描述int(x [,base])int("8") 可以轉換的包括String類型和其他數字類型&#xff0c;但是會丟失精度 float(x) float…

js高級程序設計的筆記(一)

2019獨角獸企業重金招聘Python工程師標準>>> 1.js中的 null : 如果只意在保存對象的變量還沒有真正的保存對象之前&#xff0c;就需要先把該變量保存 null值&#xff0c; null代表空對象的指針 2.函數的參數對象 arguments的理解。 argument對象的length屬性 實例 f…

Redis在PHP項目中的應用

一 運行redis服務端 出現上圖的圖形,就說明redis服務端開啟成功,并且開啟了密碼功能(如果不加載配置文件,連接redis是不需要密碼的,這樣,會給我們的程序帶來很大隱患) 密碼的設置: 在redis配置文件中,搜索requirepass ,后面設置密碼 比如 : requirepass G506myredis 則表示此…

Linux關機、重啟命令

關機shutdown -h 0 #<O秒后關機shutdown -h now #<現在關機shutdown -h 10 #<10分鐘后關機shutdown -h 23:20 #<23&#xff1a;20分關機shutdown -c #<取消shutdown關機命令init 0 #<立馬關機&#xff08;切換運行級別為0&#xff0c;推薦使用&#xff09;ha…

Python 模塊之 string.py

用法 字符串常量&#xff1a; import string print(string.ascii_lowercase) print(string.ascii_uppercase) print(string.ascii_letters) print(string.digits) print(string.hexdigits) print(string.octdigits) print(string.punctuation) print(string.printable)結果 abc…

md5和password_hash孰好孰壞

md5自不必說,想必所有phper都用過,但另一個加密函數不知道你見過沒有 他就是password_hash(); 加密方式更全面.使用起來也比md5salt這種方式要簡潔很多,最主要的是難破解,因為它每時每刻都在變動 它的使用方式大致是這樣 1. 用戶注冊時提交過來密碼,我們對用戶密碼進行加密…

Logstash 命令行參數

Logstash 提供了一個 shell 腳本叫 logstash 方便快速運行。它支持以下參數&#xff1a; -e意即執行。我們在 “Hello World” 的時候已經用過這個參數了。事實上你可以不寫任何具體配置&#xff0c;直接運行 bin/logstash -e ” 達到相同效果。這個參數的默認值是下面這樣&…

上汽拒絕HW符合商業邏輯

不久前&#xff0c;上汽董事長陳虹表示“與HW合作自動駕駛是不可接受的。這就好比一家公司為我們提供整體的解決方案&#xff0c;如此一來&#xff0c;它就成了靈魂&#xff0c;而上汽就成了軀體。對于這樣的結果&#xff0c;上汽是不能接受的&#xff0c;要把靈魂掌握在自己手…

javac compiling error ( mising package)

javac 編譯java源文件時&#xff0c;提示 package does not exist 的錯誤 Test.java import java.security.MessageDigest; import org.apache.commons.codec.binary.Hex;public class Test{public static void main(String args[]){boolean isAlarmed true;boolean aa isAla…

使用ffmpeg錄音

官方教程&#xff1a;http://ffmpeg.org/ffmpeg.html 錄音方法&#xff1a; 開始找到了這個方法&#xff0c;但是不行呀&#xff0c;好像是沒有這個oss吧。 oss 是linux 下的聲音相關的東西&#xff0c;與alsa 一樣&#xff0c;不過oss 是商業的&#xff0c; 而/dev/dsp 是oss …

Mysql慢查詢日志的使用 和 Mysql的優化

一、生成實驗數據 原理&#xff1a;sql 蠕蟲復制&#xff08;這種生成數據方式同樣適用于數據表中有主鍵的情況&#xff09;。 insert into comic (name,pen_name,cover) select name,pen_name,cover from comic 二、慢查詢日志設置 當語句執行時間較長時&#xff0c;通過日…

Oracle 12C CDB、PDB常用管理命令

--查看PDB信息&#xff08;在CDB模式下&#xff09; show pdbs --查看所有pdb select name,open_mode from v$pdbs; --v$pdbs為PDB信息視圖 select con_id, dbid, guid, name , open_mode from v$pdbs; --切換容器 alter session set containerorcl1 --切換到PDBorcl1容器…

指定時間的月初和月末一天的寫法

DateTime dt Convert.ToDateTime("2017-2");DateTime FirstDay new DateTime(dt.Year, dt.Month, 1);//本月初1號DateTime LastDay new DateTime(dt.AddMonths(1).Year, dt.AddMonths(1).Month, 1).AddDays(-1);//下月初1號減一天本月底 轉載于:https://www.cnblo…

mysql數據表設計要點以及快門

一個sql表的設計首先要考慮的是字段 然后去考慮這個字段應該具有哪些特性, 最后,考慮哪些字段經常用于查詢,設置為索引 下面是一個比較簡單的例子,大概說明了怎么樣去構造sql語句創建一個sql表 create table user_message( id int UNSIGNED not null auto_increment COMMEN…